# HG changeset patch # User pli # Date 1544761810 0 # Fri Dec 14 04:30:10 2018 +0000 # Node ID 168b5d074034a44ca61e3250ad5dbe8d5290a152 # Parent 93b401e5bf51535012d4a8fe69bfdae06a224dc2 8212043: Add floating-point Math.min/max intrinsics Summary: Floating-point Math.min() and Math.max() intrinsics are enabled on AArch64 platform Reviewed-by: adinn, aph diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -12601,6 +12601,63 @@ %} +// Math.max(FF)F +instruct maxF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (MaxF src1 src2)); + + format %{ "fmaxs $dst, $src1, $src2" %} + ins_encode %{ + __ fmaxs(as_FloatRegister($dst$$reg), + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + + ins_pipe(fp_dop_reg_reg_s); +%} + +// Math.min(FF)F +instruct minF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (MinF src1 src2)); + + format %{ "fmins $dst, $src1, $src2" %} + ins_encode %{ + __ fmins(as_FloatRegister($dst$$reg), + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + + ins_pipe(fp_dop_reg_reg_s); +%} + +// Math.max(DD)D +instruct maxD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ + match(Set dst (MaxD src1 src2)); + + format %{ "fmaxd $dst, $src1, $src2" %} + ins_encode %{ + __ fmaxd(as_FloatRegister($dst$$reg), + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + + ins_pipe(fp_dop_reg_reg_d); +%} + +// Math.min(DD)D +instruct minD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ + match(Set dst (MinD src1 src2)); + + format %{ "fmind $dst, $src1, $src2" %} + ins_encode %{ + __ fmind(as_FloatRegister($dst$$reg), + as_FloatRegister($src1$$reg), + as_FloatRegister($src2$$reg)); + %} + + ins_pipe(fp_dop_reg_reg_d); +%} + + instruct divF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ match(Set dst (DivF src1 src2)); diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -1826,12 +1826,16 @@ INSN(fdivs, 0b000, 0b00, 0b0001); INSN(fadds, 0b000, 0b00, 0b0010); INSN(fsubs, 0b000, 0b00, 0b0011); + INSN(fmaxs, 0b000, 0b00, 0b0100); + INSN(fmins, 0b000, 0b00, 0b0101); INSN(fnmuls, 0b000, 0b00, 0b1000); INSN(fmuld, 0b000, 0b01, 0b0000); INSN(fdivd, 0b000, 0b01, 0b0001); INSN(faddd, 0b000, 0b01, 0b0010); INSN(fsubd, 0b000, 0b01, 0b0011); + INSN(fmaxd, 0b000, 0b01, 0b0100); + INSN(fmind, 0b000, 0b01, 0b0101); INSN(fnmuld, 0b000, 0b01, 0b1000); #undef INSN diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -3801,7 +3801,7 @@ "AddVB","AddVS","AddVI","AddVL","AddVF","AddVD", "AndI","AndL", "AndV", - "MaxI","MinI", + "MaxI","MinI","MaxF","MinF","MaxD","MinD", "MulI","MulL","MulF","MulD", "MulVS","MulVI","MulVL","MulVF","MulVD", "OrI","OrL", diff --git a/src/hotspot/share/classfile/vmSymbols.cpp b/src/hotspot/share/classfile/vmSymbols.cpp --- a/src/hotspot/share/classfile/vmSymbols.cpp +++ b/src/hotspot/share/classfile/vmSymbols.cpp @@ -580,6 +580,10 @@ case vmIntrinsics::_max: case vmIntrinsics::_floatToIntBits: case vmIntrinsics::_doubleToLongBits: + case vmIntrinsics::_maxF: + case vmIntrinsics::_minF: + case vmIntrinsics::_maxD: + case vmIntrinsics::_minD: if (!InlineMathNatives) return true; break; case vmIntrinsics::_fmaD: diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -748,6 +748,7 @@ do_class(java_lang_StrictMath, "java/lang/StrictMath") \ do_signature(double2_double_signature, "(DD)D") \ do_signature(double3_double_signature, "(DDD)D") \ + do_signature(float2_float_signature, "(FF)F") \ do_signature(float3_float_signature, "(FFF)F") \ do_signature(int2_int_signature, "(II)I") \ do_signature(long2_long_signature, "(JJ)J") \ @@ -794,6 +795,10 @@ do_intrinsic(_subtractExactL, java_lang_Math, subtractExact_name, long2_long_signature, F_S) \ do_intrinsic(_fmaD, java_lang_Math, fma_name, double3_double_signature, F_S) \ do_intrinsic(_fmaF, java_lang_Math, fma_name, float3_float_signature, F_S) \ + do_intrinsic(_maxF, java_lang_Math, max_name, float2_float_signature, F_S) \ + do_intrinsic(_minF, java_lang_Math, min_name, float2_float_signature, F_S) \ + do_intrinsic(_maxD, java_lang_Math, max_name, double2_double_signature, F_S) \ + do_intrinsic(_minD, java_lang_Math, min_name, double2_double_signature, F_S) \ \ do_intrinsic(_floatToRawIntBits, java_lang_Float, floatToRawIntBits_name, float_int_signature, F_S) \ do_name( floatToRawIntBits_name, "floatToRawIntBits") \ diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -249,4 +249,52 @@ virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); }; +//------------------------------MaxFNode--------------------------------------- +// Maximum of 2 floats. +class MaxFNode : public MaxNode { +public: + MaxFNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + virtual int Opcode() const; + virtual const Type *add_ring(const Type*, const Type*) const { return Type::FLOAT; } + virtual const Type *add_id() const { return TypeF::NEG_INF; } + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------MinFNode--------------------------------------- +// Minimum of 2 floats. +class MinFNode : public MaxNode { +public: + MinFNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + virtual int Opcode() const; + virtual const Type *add_ring(const Type*, const Type*) const { return Type::FLOAT; } + virtual const Type *add_id() const { return TypeF::POS_INF; } + virtual const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } +}; + +//------------------------------MaxDNode--------------------------------------- +// Maximum of 2 doubles. +class MaxDNode : public MaxNode { +public: + MaxDNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + virtual int Opcode() const; + virtual const Type *add_ring(const Type*, const Type*) const { return Type::DOUBLE; } + virtual const Type *add_id() const { return TypeD::NEG_INF; } + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + +//------------------------------MinDNode--------------------------------------- +// Minimum of 2 doubles. +class MinDNode : public MaxNode { +public: + MinDNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + virtual int Opcode() const; + virtual const Type *add_ring(const Type*, const Type*) const { return Type::DOUBLE; } + virtual const Type *add_id() const { return TypeD::POS_INF; } + virtual const Type *bottom_type() const { return Type::DOUBLE; } + virtual uint ideal_reg() const { return Op_RegD; } +}; + #endif // SHARE_VM_OPTO_ADDNODE_HPP diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -440,6 +440,18 @@ case vmIntrinsics::_isWhitespace: if (!Matcher::match_rule_supported(Op_Whitespace)) return false; break; + case vmIntrinsics::_maxF: + if (!Matcher::match_rule_supported(Op_MaxF)) return false; + break; + case vmIntrinsics::_minF: + if (!Matcher::match_rule_supported(Op_MinF)) return false; + break; + case vmIntrinsics::_maxD: + if (!Matcher::match_rule_supported(Op_MaxD)) return false; + break; + case vmIntrinsics::_minD: + if (!Matcher::match_rule_supported(Op_MinD)) return false; + break; case vmIntrinsics::_hashCode: case vmIntrinsics::_identityHashCode: case vmIntrinsics::_getClass: diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -202,6 +202,8 @@ macro(Mach) macro(MachProj) macro(MulAddS2I) +macro(MaxD) +macro(MaxF) macro(MaxI) macro(MemBarAcquire) macro(LoadFence) @@ -214,6 +216,8 @@ macro(MemBarVolatile) macro(MemBarStoreStore) macro(MergeMem) +macro(MinD) +macro(MinF) macro(MinI) macro(ModD) macro(ModF) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -325,6 +325,7 @@ bool inline_vectorizedMismatch(); bool inline_fma(vmIntrinsics::ID id); bool inline_character_compare(vmIntrinsics::ID id); + bool inline_fp_min_max(vmIntrinsics::ID id); bool inline_profileBoolean(); bool inline_isCompileConstant(); @@ -874,6 +875,12 @@ case vmIntrinsics::_isWhitespace: return inline_character_compare(intrinsic_id()); + case vmIntrinsics::_maxF: + case vmIntrinsics::_minF: + case vmIntrinsics::_maxD: + case vmIntrinsics::_minD: + return inline_fp_min_max(intrinsic_id()); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -6588,6 +6595,42 @@ return true; } +//------------------------------inline_fp_min_max------------------------------ +bool LibraryCallKit::inline_fp_min_max(vmIntrinsics::ID id) { + Node *a = NULL; + Node *b = NULL; + Node *n = NULL; + switch (id) { + case vmIntrinsics::_maxF: + case vmIntrinsics::_minF: + assert(callee()->signature()->size() == 2, "minF/maxF has 2 parameters of size 1 each."); + a = argument(0); + b = argument(1); + break; + case vmIntrinsics::_maxD: + case vmIntrinsics::_minD: + assert(callee()->signature()->size() == 4, "minD/maxD has 2 parameters of size 2 each."); + a = round_double_node(argument(0)); + b = round_double_node(argument(2)); + break; + default: + fatal_unexpected_iid(id); + break; + } + if (a->is_Con() || b->is_Con()) { + return false; + } + switch (id) { + case vmIntrinsics::_maxF: n = new MaxFNode(a, b); break; + case vmIntrinsics::_minF: n = new MinFNode(a, b); break; + case vmIntrinsics::_maxD: n = new MaxDNode(a, b); break; + case vmIntrinsics::_minD: n = new MinDNode(a, b); break; + default: fatal_unexpected_iid(id); break; + } + set_result(_gvn.transform(n)); + return true; +} + bool LibraryCallKit::inline_profileBoolean() { Node* counts = argument(1); const TypeAryPtr* ary = NULL; diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -445,9 +445,13 @@ TypeF::ZERO = TypeF::make(0.0); // Float 0 (positive zero) TypeF::ONE = TypeF::make(1.0); // Float 1 + TypeF::POS_INF = TypeF::make(1.0/0.0); // Float positive infinity + TypeF::NEG_INF = TypeF::make(-1.0/0.0); // Float negative infinity TypeD::ZERO = TypeD::make(0.0); // Double 0 (positive zero) TypeD::ONE = TypeD::make(1.0); // Double 1 + TypeD::POS_INF = TypeD::make(1.0/0.0); // Double positive infinity + TypeD::NEG_INF = TypeD::make(-1.0/0.0); // Double negative infinity TypeInt::MINUS_1 = TypeInt::make(-1); // -1 TypeInt::ZERO = TypeInt::make( 0); // 0 @@ -1087,6 +1091,8 @@ // Convenience common pre-built types. const TypeF *TypeF::ZERO; // Floating point zero const TypeF *TypeF::ONE; // Floating point one +const TypeF *TypeF::POS_INF; // Floating point positive infinity +const TypeF *TypeF::NEG_INF; // Floating point negative infinity //------------------------------make------------------------------------------- // Create a float constant @@ -1195,6 +1201,8 @@ // Convenience common pre-built types. const TypeD *TypeD::ZERO; // Floating point zero const TypeD *TypeD::ONE; // Floating point one +const TypeD *TypeD::POS_INF; // Floating point positive infinity +const TypeD *TypeD::NEG_INF; // Floating point negative infinity //------------------------------make------------------------------------------- const TypeD *TypeD::make(double d) { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -483,6 +483,8 @@ // Convenience common pre-built types. static const TypeF *ZERO; // positive zero only static const TypeF *ONE; + static const TypeF *POS_INF; + static const TypeF *NEG_INF; #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif @@ -510,6 +512,8 @@ // Convenience common pre-built types. static const TypeD *ZERO; // positive zero only static const TypeD *ONE; + static const TypeD *POS_INF; + static const TypeD *NEG_INF; #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1516,6 +1516,10 @@ declare_c2_type(MaxNode, AddNode) \ declare_c2_type(MaxINode, MaxNode) \ declare_c2_type(MinINode, MaxNode) \ + declare_c2_type(MaxFNode, MaxNode) \ + declare_c2_type(MinFNode, MaxNode) \ + declare_c2_type(MaxDNode, MaxNode) \ + declare_c2_type(MinDNode, MaxNode) \ declare_c2_type(StartNode, MultiNode) \ declare_c2_type(StartOSRNode, StartNode) \ declare_c2_type(ParmNode, ProjNode) \ diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -1460,6 +1460,7 @@ * @param b another argument. * @return the larger of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static float max(float a, float b) { if (a != a) return a; // a is NaN @@ -1486,6 +1487,7 @@ * @param b another argument. * @return the larger of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static double max(double a, double b) { if (a != a) return a; // a is NaN @@ -1541,6 +1543,7 @@ * @param b another argument. * @return the smaller of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static float min(float a, float b) { if (a != a) return a; // a is NaN @@ -1567,6 +1570,7 @@ * @param b another argument. * @return the smaller of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static double min(double a, double b) { if (a != a) return a; // a is NaN diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -1154,6 +1154,7 @@ * @param b another argument. * @return the larger of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static float max(float a, float b) { return Math.max(a, b); } @@ -1172,6 +1173,7 @@ * @param b another argument. * @return the larger of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static double max(double a, double b) { return Math.max(a, b); } @@ -1219,6 +1221,7 @@ * @param b another argument. * @return the smaller of {@code a} and {@code b.} */ + @HotSpotIntrinsicCandidate public static float min(float a, float b) { return Math.min(a, b); } @@ -1237,6 +1240,7 @@ * @param b another argument. * @return the smaller of {@code a} and {@code b}. */ + @HotSpotIntrinsicCandidate public static double min(double a, double b) { return Math.min(a, b); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxIntrinsics.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxIntrinsics.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 compiler.intrinsics.math.TestFpMinMaxIntrinsics + * @run main/othervm -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:TieredStopAtLevel=1 + * -XX:CompileOnly=java/lang/Math + * compiler.intrinsics.math.TestFpMinMaxIntrinsics + * @run main/othervm -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:-TieredCompilation + * -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) { + Arrays.stream(f_cases).forEach(TestFpMinMaxIntrinsics::fTest); + Arrays.stream(d_cases).forEach(TestFpMinMaxIntrinsics::dTest); + System.out.println("PASS"); + } +} +