8285973: x86_64: Improve fp comparison and cmove for eq/ne
Reviewed-by: kvn, sviswanathan
This commit is contained in:
parent
782ae3801c
commit
c1db70d827
@ -4268,30 +4268,33 @@ operand cmpOpU()
|
||||
equal(0x4, "e");
|
||||
not_equal(0x5, "ne");
|
||||
less(0x2, "b");
|
||||
greater_equal(0x3, "nb");
|
||||
greater_equal(0x3, "ae");
|
||||
less_equal(0x6, "be");
|
||||
greater(0x7, "nbe");
|
||||
greater(0x7, "a");
|
||||
overflow(0x0, "o");
|
||||
no_overflow(0x1, "no");
|
||||
%}
|
||||
%}
|
||||
|
||||
|
||||
// Floating comparisons that don't require any fixup for the unordered case
|
||||
// Floating comparisons that don't require any fixup for the unordered case,
|
||||
// If both inputs of the comparison are the same, ZF is always set so we
|
||||
// don't need to use cmpOpUCF2 for eq/ne
|
||||
operand cmpOpUCF() %{
|
||||
match(Bool);
|
||||
predicate(n->as_Bool()->_test._test == BoolTest::lt ||
|
||||
n->as_Bool()->_test._test == BoolTest::ge ||
|
||||
n->as_Bool()->_test._test == BoolTest::le ||
|
||||
n->as_Bool()->_test._test == BoolTest::gt);
|
||||
n->as_Bool()->_test._test == BoolTest::gt ||
|
||||
n->in(1)->in(1) == n->in(1)->in(2));
|
||||
format %{ "" %}
|
||||
interface(COND_INTER) %{
|
||||
equal(0x4, "e");
|
||||
not_equal(0x5, "ne");
|
||||
equal(0xb, "np");
|
||||
not_equal(0xa, "p");
|
||||
less(0x2, "b");
|
||||
greater_equal(0x3, "nb");
|
||||
greater_equal(0x3, "ae");
|
||||
less_equal(0x6, "be");
|
||||
greater(0x7, "nbe");
|
||||
greater(0x7, "a");
|
||||
overflow(0x0, "o");
|
||||
no_overflow(0x1, "no");
|
||||
%}
|
||||
@ -4301,16 +4304,17 @@ operand cmpOpUCF() %{
|
||||
// Floating comparisons that can be fixed up with extra conditional jumps
|
||||
operand cmpOpUCF2() %{
|
||||
match(Bool);
|
||||
predicate(n->as_Bool()->_test._test == BoolTest::ne ||
|
||||
n->as_Bool()->_test._test == BoolTest::eq);
|
||||
predicate((n->as_Bool()->_test._test == BoolTest::ne ||
|
||||
n->as_Bool()->_test._test == BoolTest::eq) &&
|
||||
n->in(1)->in(1) != n->in(1)->in(2));
|
||||
format %{ "" %}
|
||||
interface(COND_INTER) %{
|
||||
equal(0x4, "e");
|
||||
not_equal(0x5, "ne");
|
||||
less(0x2, "b");
|
||||
greater_equal(0x3, "nb");
|
||||
greater_equal(0x3, "ae");
|
||||
less_equal(0x6, "be");
|
||||
greater(0x7, "nbe");
|
||||
greater(0x7, "a");
|
||||
overflow(0x0, "o");
|
||||
no_overflow(0x1, "no");
|
||||
%}
|
||||
@ -7037,6 +7041,36 @@ instruct cmovI_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
|
||||
%}
|
||||
%}
|
||||
|
||||
instruct cmovI_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
|
||||
match(Set dst (CMoveI (Binary cop cr) (Binary dst src)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpl $dst, $src\n\t"
|
||||
"cmovnel $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovl(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovl(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
|
||||
// inputs of the CMove
|
||||
instruct cmovI_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
|
||||
match(Set dst (CMoveI (Binary cop cr) (Binary src dst)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpl $dst, $src\n\t"
|
||||
"cmovnel $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovl(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovl(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// Conditional move
|
||||
instruct cmovI_mem(cmpOp cop, rFlagsReg cr, rRegI dst, memory src) %{
|
||||
match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src))));
|
||||
@ -7104,6 +7138,36 @@ instruct cmovN_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegN dst, rRegN src) %{
|
||||
%}
|
||||
%}
|
||||
|
||||
instruct cmovN_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegN dst, rRegN src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
|
||||
match(Set dst (CMoveN (Binary cop cr) (Binary dst src)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpl $dst, $src\n\t"
|
||||
"cmovnel $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovl(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovl(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
|
||||
// inputs of the CMove
|
||||
instruct cmovN_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegN dst, rRegN src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
|
||||
match(Set dst (CMoveN (Binary cop cr) (Binary src dst)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpl $dst, $src\n\t"
|
||||
"cmovnel $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovl(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovl(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// Conditional move
|
||||
instruct cmovP_reg(rRegP dst, rRegP src, rFlagsReg cr, cmpOp cop)
|
||||
%{
|
||||
@ -7138,6 +7202,36 @@ instruct cmovP_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
|
||||
%}
|
||||
%}
|
||||
|
||||
instruct cmovP_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
|
||||
match(Set dst (CMoveP (Binary cop cr) (Binary dst src)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpq $dst, $src\n\t"
|
||||
"cmovneq $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovq(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovq(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
|
||||
// inputs of the CMove
|
||||
instruct cmovP_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
|
||||
match(Set dst (CMoveP (Binary cop cr) (Binary src dst)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpq $dst, $src\n\t"
|
||||
"cmovneq $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovq(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovq(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// DISABLED: Requires the ADLC to emit a bottom_type call that
|
||||
// correctly meets the two pointer arguments; one is an incoming
|
||||
// register but the other is a memory operand. ALSO appears to
|
||||
@ -7209,6 +7303,36 @@ instruct cmovL_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
|
||||
%}
|
||||
%}
|
||||
|
||||
instruct cmovL_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
|
||||
match(Set dst (CMoveL (Binary cop cr) (Binary dst src)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpq $dst, $src\n\t"
|
||||
"cmovneq $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovq(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovq(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
|
||||
// inputs of the CMove
|
||||
instruct cmovL_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
|
||||
predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
|
||||
match(Set dst (CMoveL (Binary cop cr) (Binary src dst)));
|
||||
|
||||
ins_cost(200); // XXX
|
||||
format %{ "cmovpq $dst, $src\n\t"
|
||||
"cmovneq $dst, $src" %}
|
||||
ins_encode %{
|
||||
__ cmovq(Assembler::parity, $dst$$Register, $src$$Register);
|
||||
__ cmovq(Assembler::notEqual, $dst$$Register, $src$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_cmov_reg);
|
||||
%}
|
||||
|
||||
instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src)
|
||||
%{
|
||||
match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src))));
|
||||
@ -10479,11 +10603,12 @@ instruct and_cmpLTMask(rRegI p, rRegI q, rRegI y, rFlagsReg cr)
|
||||
|
||||
//---------- FP Instructions------------------------------------------------
|
||||
|
||||
// Really expensive, avoid
|
||||
instruct cmpF_cc_reg(rFlagsRegU cr, regF src1, regF src2)
|
||||
%{
|
||||
match(Set cr (CmpF src1 src2));
|
||||
|
||||
ins_cost(145);
|
||||
ins_cost(500);
|
||||
format %{ "ucomiss $src1, $src2\n\t"
|
||||
"jnp,s exit\n\t"
|
||||
"pushfq\t# saw NaN, set CF\n\t"
|
||||
@ -10508,24 +10633,6 @@ instruct cmpF_cc_reg_CF(rFlagsRegUCF cr, regF src1, regF src2) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpF_cc_mem(rFlagsRegU cr, regF src1, memory src2)
|
||||
%{
|
||||
match(Set cr (CmpF src1 (LoadF src2)));
|
||||
|
||||
ins_cost(145);
|
||||
format %{ "ucomiss $src1, $src2\n\t"
|
||||
"jnp,s exit\n\t"
|
||||
"pushfq\t# saw NaN, set CF\n\t"
|
||||
"andq [rsp], #0xffffff2b\n\t"
|
||||
"popfq\n"
|
||||
"exit:" %}
|
||||
ins_encode %{
|
||||
__ ucomiss($src1$$XMMRegister, $src2$$Address);
|
||||
emit_cmpfp_fixup(_masm);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpF_cc_memCF(rFlagsRegUCF cr, regF src1, memory src2) %{
|
||||
match(Set cr (CmpF src1 (LoadF src2)));
|
||||
|
||||
@ -10537,23 +10644,6 @@ instruct cmpF_cc_memCF(rFlagsRegUCF cr, regF src1, memory src2) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpF_cc_imm(rFlagsRegU cr, regF src, immF con) %{
|
||||
match(Set cr (CmpF src con));
|
||||
|
||||
ins_cost(145);
|
||||
format %{ "ucomiss $src, [$constantaddress]\t# load from constant table: float=$con\n\t"
|
||||
"jnp,s exit\n\t"
|
||||
"pushfq\t# saw NaN, set CF\n\t"
|
||||
"andq [rsp], #0xffffff2b\n\t"
|
||||
"popfq\n"
|
||||
"exit:" %}
|
||||
ins_encode %{
|
||||
__ ucomiss($src$$XMMRegister, $constantaddress($con));
|
||||
emit_cmpfp_fixup(_masm);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpF_cc_immCF(rFlagsRegUCF cr, regF src, immF con) %{
|
||||
match(Set cr (CmpF src con));
|
||||
ins_cost(100);
|
||||
@ -10564,11 +10654,12 @@ instruct cmpF_cc_immCF(rFlagsRegUCF cr, regF src, immF con) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// Really expensive, avoid
|
||||
instruct cmpD_cc_reg(rFlagsRegU cr, regD src1, regD src2)
|
||||
%{
|
||||
match(Set cr (CmpD src1 src2));
|
||||
|
||||
ins_cost(145);
|
||||
ins_cost(500);
|
||||
format %{ "ucomisd $src1, $src2\n\t"
|
||||
"jnp,s exit\n\t"
|
||||
"pushfq\t# saw NaN, set CF\n\t"
|
||||
@ -10593,24 +10684,6 @@ instruct cmpD_cc_reg_CF(rFlagsRegUCF cr, regD src1, regD src2) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpD_cc_mem(rFlagsRegU cr, regD src1, memory src2)
|
||||
%{
|
||||
match(Set cr (CmpD src1 (LoadD src2)));
|
||||
|
||||
ins_cost(145);
|
||||
format %{ "ucomisd $src1, $src2\n\t"
|
||||
"jnp,s exit\n\t"
|
||||
"pushfq\t# saw NaN, set CF\n\t"
|
||||
"andq [rsp], #0xffffff2b\n\t"
|
||||
"popfq\n"
|
||||
"exit:" %}
|
||||
ins_encode %{
|
||||
__ ucomisd($src1$$XMMRegister, $src2$$Address);
|
||||
emit_cmpfp_fixup(_masm);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpD_cc_memCF(rFlagsRegUCF cr, regD src1, memory src2) %{
|
||||
match(Set cr (CmpD src1 (LoadD src2)));
|
||||
|
||||
@ -10622,23 +10695,6 @@ instruct cmpD_cc_memCF(rFlagsRegUCF cr, regD src1, memory src2) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpD_cc_imm(rFlagsRegU cr, regD src, immD con) %{
|
||||
match(Set cr (CmpD src con));
|
||||
|
||||
ins_cost(145);
|
||||
format %{ "ucomisd $src, [$constantaddress]\t# load from constant table: double=$con\n\t"
|
||||
"jnp,s exit\n\t"
|
||||
"pushfq\t# saw NaN, set CF\n\t"
|
||||
"andq [rsp], #0xffffff2b\n\t"
|
||||
"popfq\n"
|
||||
"exit:" %}
|
||||
ins_encode %{
|
||||
__ ucomisd($src$$XMMRegister, $constantaddress($con));
|
||||
emit_cmpfp_fixup(_masm);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpD_cc_immCF(rFlagsRegUCF cr, regD src, immD con) %{
|
||||
match(Set cr (CmpD src con));
|
||||
ins_cost(100);
|
||||
|
361
test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison.java
Normal file
361
test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison.java
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. 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.
|
||||
*/
|
||||
package compiler.c2.irTests;
|
||||
|
||||
import compiler.lib.ir_framework.*;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8285973
|
||||
* @summary Test that code generation for fp comparison works as intended
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.c2.irTests.TestFPComparison
|
||||
*/
|
||||
public class TestFPComparison {
|
||||
static final double[] DOUBLES = new double[] {
|
||||
Double.NEGATIVE_INFINITY,
|
||||
-Double.MAX_VALUE,
|
||||
-1.0,
|
||||
-Double.MIN_VALUE,
|
||||
-0.0,
|
||||
0.0,
|
||||
Double.MIN_VALUE,
|
||||
1.0,
|
||||
Double.MAX_VALUE,
|
||||
Double.POSITIVE_INFINITY,
|
||||
Double.NaN,
|
||||
};
|
||||
|
||||
static final float[] FLOATS = new float[] {
|
||||
Float.NEGATIVE_INFINITY,
|
||||
-Float.MAX_VALUE,
|
||||
-1.0F,
|
||||
-Float.MIN_VALUE,
|
||||
-0.0F,
|
||||
0.0F,
|
||||
Float.MIN_VALUE,
|
||||
1.0F,
|
||||
Float.MAX_VALUE,
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.NaN,
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
@DontInline
|
||||
static int call() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveEqualTwoDoubles(double x, double y) {
|
||||
return x == y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveEqualTwoFloats(float x, float y) {
|
||||
return x == y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveNotEqualTwoDoubles(double x, double y) {
|
||||
return x != y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveNotEqualTwoFloats(float x, float y) {
|
||||
return x != y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveLessThanTwoDoubles(double x, double y) {
|
||||
return x < y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveLessThanTwoFloats(float x, float y) {
|
||||
return x < y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveMoreThanTwoDoubles(double x, double y) {
|
||||
return x > y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveMoreThanTwoFloats(float x, float y) {
|
||||
return x > y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveLessEqualTwoDoubles(double x, double y) {
|
||||
return x <= y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveLessEqualTwoFloats(float x, float y) {
|
||||
return x <= y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveMoreEqualTwoDoubles(double x, double y) {
|
||||
return x >= y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveMoreEqualTwoFloats(float x, float y) {
|
||||
return x >= y ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveEqualOneDouble(double x) {
|
||||
return x == x ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveEqualOneFloat(float x) {
|
||||
return x == x ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveNotEqualOneDouble(double x) {
|
||||
return x != x ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.CMOVEI, "1"})
|
||||
public int cMoveNotEqualOneFloat(float x) {
|
||||
return x != x ? 1 : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchEqualTwoDoubles(double x, double y) {
|
||||
return x == y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchEqualTwoFloats(float x, float y) {
|
||||
return x == y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchNotEqualTwoDoubles(double x, double y) {
|
||||
return x != y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchNotEqualTwoFloats(float x, float y) {
|
||||
return x != y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchLessThanTwoDoubles(double x, double y) {
|
||||
return x < y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchLessThanTwoFloats(float x, float y) {
|
||||
return x < y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchMoreThanTwoDoubles(double x, double y) {
|
||||
return x > y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchMoreThanTwoFloats(float x, float y) {
|
||||
return x > y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchLessEqualTwoDoubles(double x, double y) {
|
||||
return x <= y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchLessEqualTwoFloats(float x, float y) {
|
||||
return x <= y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchMoreEqualTwoDoubles(double x, double y) {
|
||||
return x >= y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchMoreEqualTwoFloats(float x, float y) {
|
||||
return x >= y ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchEqualOneDouble(double x) {
|
||||
return x == x ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchEqualOneFloat(float x) {
|
||||
return x == x ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchNotEqualOneDouble(double x) {
|
||||
return x != x ? call() : 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.IF, "1"})
|
||||
public int branchNotEqualOneFloat(float x) {
|
||||
return x != x ? call() : 0;
|
||||
}
|
||||
|
||||
@Run(test = {"cMoveEqualTwoDoubles", "cMoveEqualTwoFloats", "cMoveNotEqualTwoDoubles", "cMoveNotEqualTwoFloats",
|
||||
"cMoveLessThanTwoDoubles", "cMoveLessThanTwoFloats", "cMoveMoreThanTwoDoubles", "cMoveMoreThanTwoFloats",
|
||||
"cMoveLessEqualTwoDoubles", "cMoveLessEqualTwoFloats", "cMoveMoreEqualTwoDoubles", "cMoveMoreEqualTwoFloats",
|
||||
"cMoveEqualOneDouble", "cMoveEqualOneFloat", "cMoveNotEqualOneDouble", "cMoveNotEqualOneFloat",
|
||||
"branchEqualTwoDoubles", "branchEqualTwoFloats", "branchNotEqualTwoDoubles", "branchNotEqualTwoFloats",
|
||||
"branchLessThanTwoDoubles", "branchLessThanTwoFloats", "branchMoreThanTwoDoubles", "branchMoreThanTwoFloats",
|
||||
"branchLessEqualTwoDoubles", "branchLessEqualTwoFloats", "branchMoreEqualTwoDoubles", "branchMoreEqualTwoFloats",
|
||||
"branchEqualOneDouble", "branchEqualOneFloat", "branchNotEqualOneDouble", "branchNotEqualOneFloat"})
|
||||
public void runTests() {
|
||||
for (int i = 0; i < DOUBLES.length; i++) {
|
||||
for (int j = 0; j < DOUBLES.length; j++) {
|
||||
int len = DOUBLES.length;
|
||||
double x = DOUBLES[i];
|
||||
double y = DOUBLES[j];
|
||||
Asserts.assertEquals(cMoveEqualTwoDoubles(x, x),
|
||||
(x == x) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveNotEqualTwoDoubles(x, x),
|
||||
(x != x) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveEqualTwoDoubles(x, y),
|
||||
(x == y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveNotEqualTwoDoubles(x, y),
|
||||
(x != y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveLessThanTwoDoubles(x, y),
|
||||
(x < y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveLessEqualTwoDoubles(x, y),
|
||||
(x <= y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveMoreThanTwoDoubles(x, y),
|
||||
(x > y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveMoreEqualTwoDoubles(x, y),
|
||||
(x >= y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchEqualTwoDoubles(x, y),
|
||||
(x == y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchNotEqualTwoDoubles(x, y),
|
||||
(x != y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchLessThanTwoDoubles(x, y),
|
||||
(x < y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchLessEqualTwoDoubles(x, y),
|
||||
(x <= y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchMoreThanTwoDoubles(x, y),
|
||||
(x > y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchMoreEqualTwoDoubles(x, y),
|
||||
(x >= y) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < FLOATS.length; i++) {
|
||||
for (int j = 0; j < FLOATS.length; j++) {
|
||||
int len = FLOATS.length;
|
||||
float x = FLOATS[i];
|
||||
float y = FLOATS[j];
|
||||
Asserts.assertEquals(cMoveEqualTwoFloats(x, x),
|
||||
(x == x) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveNotEqualTwoFloats(x, x),
|
||||
(x != x) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveEqualTwoFloats(x, y),
|
||||
(x == y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveNotEqualTwoFloats(x, y),
|
||||
(x != y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveLessThanTwoFloats(x, y),
|
||||
(x < y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveLessEqualTwoFloats(x, y),
|
||||
(x <= y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveMoreThanTwoFloats(x, y),
|
||||
(x > y) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveMoreEqualTwoFloats(x, y),
|
||||
(x >= y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchEqualTwoFloats(x, y),
|
||||
(x == y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchNotEqualTwoFloats(x, y),
|
||||
(x != y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchLessThanTwoFloats(x, y),
|
||||
(x < y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchLessEqualTwoFloats(x, y),
|
||||
(x <= y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchMoreThanTwoFloats(x, y),
|
||||
(x > y) ? 1 : 0);
|
||||
Asserts.assertEquals(branchMoreEqualTwoFloats(x, y),
|
||||
(x >= y) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < DOUBLES.length; i++) {
|
||||
Asserts.assertEquals(cMoveEqualOneDouble(DOUBLES[DOUBLES.length - 1]), 0);
|
||||
Asserts.assertEquals(cMoveNotEqualOneDouble(DOUBLES[DOUBLES.length - 1]), 1);
|
||||
Asserts.assertEquals(cMoveEqualOneDouble(DOUBLES[i]), (i != DOUBLES.length - 1) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveNotEqualOneDouble(DOUBLES[i]), (i == DOUBLES.length - 1) ? 1 : 0);
|
||||
Asserts.assertEquals(branchEqualOneDouble(DOUBLES[i]), (i != DOUBLES.length - 1) ? 1 : 0);
|
||||
Asserts.assertEquals(branchNotEqualOneDouble(DOUBLES[i]), (i == DOUBLES.length - 1) ? 1 : 0);
|
||||
}
|
||||
for (int i = 0; i < FLOATS.length; i++) {
|
||||
Asserts.assertEquals(cMoveEqualOneFloat(FLOATS[FLOATS.length - 1]), 0);
|
||||
Asserts.assertEquals(cMoveNotEqualOneFloat(FLOATS[FLOATS.length - 1]), 1);
|
||||
Asserts.assertEquals(cMoveEqualOneFloat(FLOATS[i]), (i != FLOATS.length - 1) ? 1 : 0);
|
||||
Asserts.assertEquals(cMoveNotEqualOneFloat(FLOATS[i]), (i == FLOATS.length - 1) ? 1 : 0);
|
||||
Asserts.assertEquals(branchEqualOneFloat(FLOATS[i]), (i != FLOATS.length - 1) ? 1 : 0);
|
||||
Asserts.assertEquals(branchNotEqualOneFloat(FLOATS[i]), (i == FLOATS.length - 1) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -141,6 +141,7 @@ public class IRNode {
|
||||
public static final String MEMBAR_STORESTORE = START + "MemBarStoreStore" + MID + END;
|
||||
public static final String SAFEPOINT = START + "SafePoint" + MID + END;
|
||||
|
||||
public static final String CMOVEI = START + "CMoveI" + MID + END;
|
||||
public static final String ABS_I = START + "AbsI" + MID + END;
|
||||
public static final String ABS_L = START + "AbsL" + MID + END;
|
||||
public static final String ABS_F = START + "AbsF" + MID + END;
|
||||
|
132
test/micro/org/openjdk/bench/java/lang/FPComparison.java
Normal file
132
test/micro/org/openjdk/bench/java/lang/FPComparison.java
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. 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.
|
||||
*/
|
||||
package org.openjdk.bench.java.lang;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Thread)
|
||||
@Fork(1)
|
||||
public class FPComparison {
|
||||
static final int INVOCATIONS = 1024;
|
||||
|
||||
float[] f1;
|
||||
double[] d1;
|
||||
float[] f2;
|
||||
double[] d2;
|
||||
int[] res;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
var random = RandomGenerator.getDefault();
|
||||
f1 = new float[INVOCATIONS];
|
||||
d1 = new double[INVOCATIONS];
|
||||
f2 = new float[INVOCATIONS];
|
||||
d2 = new double[INVOCATIONS];
|
||||
res = new int[INVOCATIONS];
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
int type = random.nextInt(5);
|
||||
if (type == 1) {
|
||||
f1[i] = random.nextFloat();
|
||||
d1[i] = random.nextDouble();
|
||||
f2[i] = random.nextFloat();
|
||||
d2[i] = random.nextDouble();
|
||||
} else if (type == 2) {
|
||||
f1[i] = Float.POSITIVE_INFINITY;
|
||||
d1[i] = Double.POSITIVE_INFINITY;
|
||||
f2[i] = Float.POSITIVE_INFINITY;
|
||||
d2[i] = Double.POSITIVE_INFINITY;
|
||||
} else if (type == 3) {
|
||||
f1[i] = Float.NEGATIVE_INFINITY;
|
||||
d1[i] = Double.NEGATIVE_INFINITY;
|
||||
f2[i] = Float.NEGATIVE_INFINITY;
|
||||
d2[i] = Double.NEGATIVE_INFINITY;
|
||||
} else if (type >= 4) {
|
||||
f1[i] = Float.NaN;
|
||||
d1[i] = Double.NaN;
|
||||
f2[i] = Float.NaN;
|
||||
d2[i] = Double.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void isNanFloat() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = Float.isNaN(f1[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void isNanDouble() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = Double.isNaN(d1[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void isInfiniteFloat() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = Float.isInfinite(f1[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void isInfiniteDouble() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = Double.isInfinite(d1[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void isFiniteFloat() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = Float.isFinite(f1[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void isFiniteDouble() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = Double.isFinite(d1[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void equalFloat() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = (f1[i] == f2[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void equalDouble() {
|
||||
for (int i = 0; i < INVOCATIONS; i++) {
|
||||
res[i] = (d1[i] == d2[i]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user