8285973: x86_64: Improve fp comparison and cmove for eq/ne

Reviewed-by: kvn, sviswanathan
This commit is contained in:
Quan Anh Mai 2022-05-24 00:15:54 +00:00 committed by Sandhya Viswanathan
parent 782ae3801c
commit c1db70d827
4 changed files with 634 additions and 84 deletions

View File

@ -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);

View 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);
}
}
}

View File

@ -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;

View 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;
}
}
}