--- old/src/hotspot/cpu/x86/assembler_x86.cpp 2019-02-04 21:47:03.777292460 +0530 +++ new/src/hotspot/cpu/x86/assembler_x86.cpp 2019-02-04 21:47:03.589292456 +0530 @@ -7767,7 +7767,8 @@ void Assembler::cmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { assert(VM_Version::supports_avx(), ""); - assert(!VM_Version::supports_evex(), ""); + assert(vector_len < 2, ""); + assert((dst->encoding() < 16 && nds->encoding() < 16 && src->encoding() < 16) ,"XMM register should be 0-15"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, nds, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xC2); @@ -7775,9 +7776,44 @@ emit_int8((unsigned char)(0xF & cop)); } +void Assembler::vmaxps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5F); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::vmaxpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_rex_vex_w_reverted(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5F); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::vminps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5D); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::vminpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_rex_vex_w_reverted(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5D); + emit_int8((unsigned char)(0xC0 | encode)); +} + void Assembler::blendvpd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { assert(VM_Version::supports_avx(), ""); - assert(!VM_Version::supports_evex(), ""); + assert(vector_len < 2, ""); + assert((dst->encoding() < 16 && nds->encoding() < 16 && src1->encoding() < 16 && src2->encoding() < 16) ,"XMM register should be 0-15"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0x4B); @@ -7788,7 +7824,8 @@ void Assembler::cmpps(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { assert(VM_Version::supports_avx(), ""); - assert(!VM_Version::supports_evex(), ""); + assert(vector_len < 2, ""); + assert((dst->encoding() < 16 && nds->encoding() < 16 && src->encoding() < 16) ,"XMM register should be 0-15"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, nds, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xC2); @@ -7798,7 +7835,8 @@ void Assembler::blendvps(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { assert(VM_Version::supports_avx(), ""); - assert(!VM_Version::supports_evex(), ""); + assert(vector_len < 2, ""); + assert((dst->encoding() < 16 && nds->encoding() < 16 && src1->encoding() < 16 && src2->encoding() < 16) ,"XMM register should be 0-15"); InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ true); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0x4A); --- old/src/hotspot/cpu/x86/assembler_x86.hpp 2019-02-04 21:47:04.153292469 +0530 +++ new/src/hotspot/cpu/x86/assembler_x86.hpp 2019-02-04 21:47:03.965292465 +0530 @@ -1934,6 +1934,11 @@ void vsubss(XMMRegister dst, XMMRegister nds, Address src); void vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src); + void vmaxps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vmaxpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vminps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vminpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void shlxl(Register dst, Register src1, Register src2); void shlxq(Register dst, Register src1, Register src2); --- old/src/hotspot/cpu/x86/x86.ad 2019-02-04 21:47:04.657292481 +0530 +++ new/src/hotspot/cpu/x86/x86.ad 2019-02-04 21:47:04.469292477 +0530 @@ -1450,6 +1450,13 @@ if (UseSSE < 2) ret_value = false; break; + case Op_MaxD: + case Op_MaxF: + case Op_MinD: + case Op_MinF: + if (UseAVX < 1) // enabled for AVX only + ret_value = false; + break; } return ret_value; // Per default match rules are supported. @@ -2838,6 +2845,105 @@ %} ins_pipe( pipe_slow ); %} + +// Following pseudo code describes the algorithm for max[FD]: +// Min algorithm is on similar lines +// btmp = (b < 0) ? a : b +// atmp = (b < 0) ? b : a +// Tmp = Max_Float( atmp , btmp) +// Res = (atmp == NaN) ? atmp : Tmp +instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ + predicate(UseAVX > 0); + match(Set dst (MaxF a b)); + effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); + format %{ + "blendvps $btmp,$b,$a,$b \n\t" + "blendvps $atmp,$a,$b,$b \n\t" + "vmaxps $tmp,$atmp,$btmp \n\t" + "cmpps.unordered $btmp, $atmp, $atmp \n\t" + "blendvps $dst,$tmp,$atmp,$btmp \n\t" + %} + ins_encode %{ + int vector_len = 0; + __ blendvps($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, vector_len); + __ blendvps($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $b$$XMMRegister, vector_len); + __ vmaxps($tmp$$XMMRegister, $atmp$$XMMRegister , $btmp$$XMMRegister, vector_len); + __ cmpps($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, 0x3, vector_len); + __ blendvps($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + + +// max = java.lang.Max(double a , double b) +instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ + predicate(UseAVX > 0); + match(Set dst (MaxD a b)); + effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); + format %{ + "blendvpd $btmp,$b,$a,$b \n\t" + "blendvpd $atmp,$a,$b,$b \n\t" + "vmaxpd $tmp,$atmp,$btmp \n\t" + "cmppd.unordered $btmp, $atmp, $atmp \n\t" + "blendvpd $dst,$tmp,$atmp,$btmp \n\t" + %} + ins_encode %{ + int vector_len = 0; + __ blendvpd($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, vector_len); + __ blendvpd($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $b$$XMMRegister, vector_len); + __ vmaxpd($tmp$$XMMRegister, $atmp$$XMMRegister , $btmp$$XMMRegister, vector_len); + __ cmppd($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, 0x3, vector_len); + __ blendvpd($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + + +// min = java.lang.Min(float a , float b) +instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ + predicate(UseAVX > 0); + match(Set dst (MinF a b)); + effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); + format %{ + "blendvps $atmp,$a,$b,$a \n\t" + "blendvps $btmp,$b,$a,$a \n\t" + "vminps $tmp,$atmp,$btmp \n\t" + "cmpps.unordered $btmp, $atmp, $atmp \n\t" + "blendvps $dst,$tmp,$atmp,$btmp \n\t" + %} + ins_encode %{ + int vector_len = 0; + __ blendvps($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, vector_len); + __ blendvps($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $a$$XMMRegister, vector_len); + __ vminps($tmp$$XMMRegister, $atmp$$XMMRegister , $btmp$$XMMRegister, vector_len); + __ cmpps($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, 0x3, vector_len); + __ blendvps($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +// min = java.lang.Min(double a , double b) +instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ + predicate(UseAVX > 0); + match(Set dst (MinD a b)); + effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); + format %{ + "blendvpd $atmp,$a,$b,$a \n\t" + "blendvpd $btmp,$b,$a,$a \n\t" + "vminpd $tmp,$atmp,$btmp \n\t" + "cmppd.unordered $btmp, $atmp, $atmp \n\t" + "blendvpd $dst,$tmp,$atmp,$btmp \n\t" + %} + ins_encode %{ + int vector_len = 0; + __ blendvpd($atmp$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, vector_len); + __ blendvpd($btmp$$XMMRegister, $b$$XMMRegister, $a$$XMMRegister, $a$$XMMRegister, vector_len); + __ vminpd($tmp$$XMMRegister, $atmp$$XMMRegister , $btmp$$XMMRegister, vector_len); + __ cmppd($btmp$$XMMRegister, $atmp$$XMMRegister, $atmp$$XMMRegister, 0x3, vector_len); + __ blendvpd($dst$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} // ====================VECTOR INSTRUCTIONS===================================== --- old/src/hotspot/cpu/x86/x86_32.ad 2019-02-04 21:47:05.181292494 +0530 +++ new/src/hotspot/cpu/x86/x86_32.ad 2019-02-04 21:47:04.993292489 +0530 @@ -4376,6 +4376,24 @@ opclass load_long_memory(load_long_indirect, load_long_indOffset32); + +operand legRegF() %{ + predicate( UseSSE>=1 ); + constraint(ALLOC_IN_RC(float_reg_legacy)); + match(RegF); + format %{ %} + interface(REG_INTER); +%} + +operand legRegD() %{ + predicate( UseSSE>=2 ); + constraint(ALLOC_IN_RC(double_reg_legacy)); + match(RegD); + format %{ %} + interface(REG_INTER); +%} + + //----------Special Memory Operands-------------------------------------------- // Stack Slot Operand - This operand is used for loading and storing temporary @@ -5982,6 +6000,32 @@ %} // Load Float +instruct MoveF2LEG(legRegF dst, regF src) %{ + match(Set dst src); + format %{ "movss $dst,$src\t! if src != dst load float (4 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Float +instruct MoveLEG2F(regF dst, legRegF src) %{ + match(Set dst src); + format %{ "movss $dst,$src\t! if src != dst load float (4 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + + + +// Load Float instruct loadFPR(regFPR dst, memory mem) %{ predicate(UseSSE==0); match(Set dst (LoadF mem)); @@ -6212,6 +6256,31 @@ ins_pipe(pipe_slow); %} +// Load Double +instruct MoveD2LEG(legRegD dst, regD src) %{ + match(Set dst src); + format %{ "movsd $dst,$src\t! if src != dst load double (8 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Double +instruct MoveLEG2D(regD dst, legRegD src) %{ + match(Set dst src); + format %{ "movsd $dst,$src\t! if src != dst load double (8 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + + // The instruction usage is guarded by predicate in operand immD0(). instruct loadConD0(regD dst, immD0 src) %{ match(Set dst src); --- old/src/hotspot/cpu/x86/x86_64.ad 2019-02-04 21:47:05.709292506 +0530 +++ new/src/hotspot/cpu/x86/x86_64.ad 2019-02-04 21:47:05.525292502 +0530 @@ -3657,6 +3657,16 @@ interface(REG_INTER); %} + +// Float register operands +operand legRegF() %{ + constraint(ALLOC_IN_RC(float_reg_legacy)); + match(RegF); + + format %{ %} + interface(REG_INTER); +%} + // Float register operands operand vlRegF() %{ constraint(ALLOC_IN_RC(float_reg_vl)); @@ -3676,6 +3686,15 @@ %} // Double register operands +operand legRegD() %{ + constraint(ALLOC_IN_RC(double_reg_legacy)); + match(RegD); + + format %{ %} + interface(REG_INTER); +%} + +// Double register operands operand vlRegD() %{ constraint(ALLOC_IN_RC(double_reg_vl)); match(RegD); @@ -5403,6 +5422,7 @@ ins_pipe(pipe_slow); // XXX %} + // Load Float instruct MoveF2VL(vlRegF dst, regF src) %{ match(Set dst src); @@ -5414,6 +5434,18 @@ %} // Load Float +instruct MoveF2LEG(legRegF dst, regF src) %{ + match(Set dst src); + format %{ "movss $dst,$src\t! if src != dst load float (4 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Float instruct MoveVL2F(regF dst, vlRegF src) %{ match(Set dst src); format %{ "movss $dst,$src\t! load float (4 bytes)" %} @@ -5423,6 +5455,18 @@ ins_pipe( fpu_reg_reg ); %} +// Load Float +instruct MoveLEG2F(regF dst, legRegF src) %{ + match(Set dst src); + format %{ "movss $dst,$src\t! if src != dst load float (4 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movflt($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + // Load Double instruct loadD_partial(regD dst, memory mem) %{ @@ -5437,6 +5481,7 @@ ins_pipe(pipe_slow); // XXX %} + instruct loadD(regD dst, memory mem) %{ predicate(UseXmmLoadAndClearUpper); @@ -5461,6 +5506,18 @@ %} // Load Double +instruct MoveD2LEG(legRegD dst, regD src) %{ + match(Set dst src); + format %{ "movsd $dst,$src\t! if src != dst load double (8 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} + +// Load Double instruct MoveVL2D(regD dst, vlRegD src) %{ match(Set dst src); format %{ "movsd $dst,$src\t! load double (8 bytes)" %} @@ -5469,6 +5526,18 @@ %} ins_pipe( fpu_reg_reg ); %} + +// Load Double +instruct MoveLEG2D(regD dst, legRegD src) %{ + match(Set dst src); + format %{ "movsd $dst,$src\t! if src != dst load double (8 bytes)" %} + ins_encode %{ + if ($dst$$reg != $src$$reg) { + __ movdbl($dst$$XMMRegister, $src$$XMMRegister); + } + %} + ins_pipe( fpu_reg_reg ); +%} // Load Effective Address instruct leaP8(rRegP dst, indOffset8 mem) --- /dev/null 2019-01-21 08:42:35.435473376 +0530 +++ new/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxIntrinsics.java 2019-02-04 21:47:06.049292514 +0530 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Arm Limited. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8212043 + * @summary Test compiler intrinsics of floating-point Math.min/max + * + * @run main/othervm -Xint -XX:+IgnoreUnrecognizedVMOptions compiler.intrinsics.math.TestFpMinMaxIntrinsics + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -Xcomp -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+IgnoreUnrecognizedVMOptions -XX:CompileOnly=java/lang/Math + * compiler.intrinsics.math.TestFpMinMaxIntrinsics + * @run main/othervm -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions + * -XX:CompileOnly=java/lang/Math + * compiler.intrinsics.math.TestFpMinMaxIntrinsics + */ + +package compiler.intrinsics.math; + +import java.util.Arrays; + +public class TestFpMinMaxIntrinsics { + + private static final float fPos = 15280.0f; + private static final float fNeg = -55555.5f; + private static final float fPosZero = 0.0f; + private static final float fNegZero = -0.0f; + private static final float fPosInf = Float.POSITIVE_INFINITY; + private static final float fNegInf = Float.NEGATIVE_INFINITY; + private static final float fNaN = Float.NaN; + + private static final double dPos = 482390926662501720.0; + private static final double dNeg = -333333333333333333.3; + private static final double dPosZero = 0.0; + private static final double dNegZero = -0.0; + private static final double dPosInf = Double.POSITIVE_INFINITY; + private static final double dNegInf = Double.NEGATIVE_INFINITY; + private static final double dNaN = Double.NaN; + + private static final float[][] f_cases = { + // a b min max + { fPos, fPos, fPos, fPos }, + { fPos, fNeg, fNeg, fPos }, + { fPosZero, fNegZero, fNegZero, fPosZero }, + { fNegZero, fNegZero, fNegZero, fNegZero }, + { fPos, fPosInf, fPos, fPosInf }, + { fNeg, fNegInf, fNegInf, fNeg }, + { fPos, fNaN, fNaN, fNaN }, + { fNegInf, fNaN, fNaN, fNaN }, + }; + + private static final double[][] d_cases = { + // a b min max + { dPos, dPos, dPos, dPos }, + { dPos, dNeg, dNeg, dPos }, + { dPosZero, dNegZero, dNegZero, dPosZero }, + { dNegZero, dNegZero, dNegZero, dNegZero }, + { dPos, dPosInf, dPos, dPosInf }, + { dNeg, dNegInf, dNegInf, dNeg }, + { dPos, dNaN, dNaN, dNaN }, + { dNegInf, dNaN, dNaN, dNaN }, + }; + + private static void fTest(float[] row) { + float min = Math.min(row[0], row[1]); + float max = Math.max(row[0], row[1]); + if (Float.isNaN(min) && Float.isNaN(max) + && Float.isNaN(row[2]) && Float.isNaN(row[3])) { + // Return if all of them are NaN + return; + } + if (min != row[2] || max != row[3]) { + throw new AssertionError(("Unexpected result of float min/max: " + + "a = " + row[0] + ", b = " + row[1] + ", " + + "result = (" + min + ", " + max + "), " + + "expected = (" + row[2] + ", " + row[3] + ")")); + } + } + + private static void dTest(double[] row) { + double min = Math.min(row[0], row[1]); + double max = Math.max(row[0], row[1]); + if (Double.isNaN(min) && Double.isNaN(max) + && Double.isNaN(row[2]) && Double.isNaN(row[3])) { + // Return if all of them are NaN + return; + } + if (min != row[2] || max != row[3]) { + throw new AssertionError(("Unexpected result of double min/max: " + + "a = " + row[0] + ", b = " + row[1] + ", " + + "result = (" + min + ", " + max + "), " + + "expected = (" + row[2] + ", " + row[3] + ")")); + } + } + + public static void main(String[] args) { + for (int i = 0 ; i < 100000 ; i++) { + Arrays.stream(f_cases).forEach(TestFpMinMaxIntrinsics::fTest); + Arrays.stream(d_cases).forEach(TestFpMinMaxIntrinsics::dTest); + } + } +} +