diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 443086b6737..928e51191d6 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1080,7 +1080,7 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } -static Node* build_min_max_int(Node* a, Node* b, bool is_max) { +Node* MaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { if (is_max) { return new MaxINode(a, b); } else { @@ -1088,6 +1088,14 @@ static Node* build_min_max_int(Node* a, Node* b, bool is_max) { } } +Node* MaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max) { + if (is_max) { + return new MaxLNode(phase->C, a, b); + } else { + return new MinLNode(phase->C, a, b); + } +} + Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn) { bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 577dd6ba295..a6ef58b98ce 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -295,6 +295,9 @@ public: static Node* min_diff_with_zero(Node* a, Node* b, const Type* t, PhaseGVN& gvn) { return build_min_max_diff_with_zero(a, b, false, t, gvn); } + + static Node* build_min_max_int(Node* a, Node* b, bool is_max); + static Node* build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max); }; //------------------------------MaxINode--------------------------------------- diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index bfa30f02ada..8d9cc80e69e 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -96,6 +96,12 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { BoolNode* b = in(Condition)->as_Bool()->negate(phase); return make(in(Control), phase->transform(b), in(IfTrue), in(IfFalse), _type); } + + Node* minmax = Ideal_minmax(phase, this); + if (minmax != nullptr) { + return minmax; + } + return nullptr; } @@ -185,6 +191,65 @@ CMoveNode *CMoveNode::make(Node *c, Node *bol, Node *left, Node *right, const Ty } } +// Try to identify min/max patterns in CMoves +Node* CMoveNode::Ideal_minmax(PhaseGVN* phase, CMoveNode* cmove) { + // If we're post loop opts then don't attempt to match the min/max pattern, as this node might have been a + // MinL or MaxL that was already expanded during macro expansion. + if (phase->C->post_loop_opts_phase()) { + return nullptr; + } + + // The BoolNode may have been idealized into a constant. If that's the case, then Identity should take care of it instead. + BoolNode* bol = cmove->in(CMoveNode::Condition)->isa_Bool(); + if (bol == nullptr) { + return nullptr; + } + + Node* cmp = bol->in(1); + int cmove_op = cmove->Opcode(); + int cmp_op = cmp->Opcode(); + + // Ensure comparison is an integral type, and that the cmove is of the same type. + if (!((cmp_op == Op_CmpI && cmove_op == Op_CMoveI) || (cmp_op == Op_CmpL && cmove_op == Op_CMoveL))) { + return nullptr; + } + + // Only accept canonicalized le and lt comparisons + int test = bol->_test._test; + if (test != BoolTest::le && test != BoolTest::lt) { + return nullptr; + } + + // The values being compared + Node* cmp_l = cmp->in(1); + Node* cmp_r = cmp->in(2); + + // The values being selected + Node* cmove_l = cmove->in(CMoveNode::IfTrue); + Node* cmove_r = cmove->in(CMoveNode::IfFalse); + + // For this transformation to be valid, the values being compared must be the same as the values being selected. + // We accept two different forms, "a < b ? a : b" and "a < b ? b : a". For the first form, the lhs and rhs of the + // comparison and cmove are the same, resulting in a minimum. For the second form, the lhs and rhs of both are flipped, + // resulting in a maximum. If neither form is found, bail out. + + bool is_max; + if (cmp_l == cmove_l && cmp_r == cmove_r) { + is_max = false; + } else if (cmp_l == cmove_r && cmp_r == cmove_l) { + is_max = true; + } else { + return nullptr; + } + + // Create the Min/Max node based on the type and kind + if (cmp_op == Op_CmpL) { + return MaxNode::build_min_max_long(phase, cmp_l, cmp_r, is_max); + } else { + return MaxNode::build_min_max_int(cmp_l, cmp_r, is_max); + } +} + //============================================================================= //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. diff --git a/src/hotspot/share/opto/movenode.hpp b/src/hotspot/share/opto/movenode.hpp index 96189654649..ae1e8563da3 100644 --- a/src/hotspot/share/opto/movenode.hpp +++ b/src/hotspot/share/opto/movenode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -49,7 +49,8 @@ class CMoveNode : public TypeNode { virtual Node* Identity(PhaseGVN* phase); static CMoveNode *make(Node *c, Node *bol, Node *left, Node *right, const Type *t); // Helper function to spot cmove graph shapes - static Node *is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ); + static Node* is_cmove_id(PhaseTransform* phase, Node* cmp, Node* t, Node* f, BoolNode* b); + static Node* Ideal_minmax(PhaseGVN* phase, CMoveNode* cmov); }; //------------------------------CMoveDNode------------------------------------- diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java new file mode 100644 index 00000000000..3d4f27e4311 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2024, 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 jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import java.util.Random; +import jdk.test.lib.Utils; + +/* + * @test + * @bug 8324655 + * @summary Test that if expressions are properly folded into min/max nodes + * @library /test/lib / + * @run main compiler.c2.irTests.TestIfMinMax + */ +public class TestIfMinMax { + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MIN_I, "1" }) + public int testMinI1(int a, int b) { + return a < b ? a : b; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MIN_I, "1" }) + public int testMinI2(int a, int b) { + return a > b ? b : a; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MAX_I, "1" }) + public int testMaxI1(int a, int b) { + return a > b ? a : b; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MAX_I, "1" }) + public int testMaxI2(int a, int b) { + return a < b ? b : a; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MIN_I, "1" }) + public int testMinI1E(int a, int b) { + return a <= b ? a : b; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MIN_I, "1" }) + public int testMinI2E(int a, int b) { + return a >= b ? b : a; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MAX_I, "1" }) + public int testMaxI1E(int a, int b) { + return a >= b ? a : b; + } + + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MAX_I, "1" }) + public int testMaxI2E(int a, int b) { + return a <= b ? b : a; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MIN_L, "1" }) + public long testMinL1(long a, long b) { + return a < b ? a : b; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MIN_L, "1" }) + public long testMinL2(long a, long b) { + return a > b ? b : a; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MAX_L, "1" }) + public long testMaxL1(long a, long b) { + return a > b ? a : b; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MAX_L, "1" }) + public long testMaxL2(long a, long b) { + return a < b ? b : a; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MIN_L, "1" }) + public long testMinL1E(long a, long b) { + return a <= b ? a : b; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MIN_L, "1" }) + public long testMinL2E(long a, long b) { + return a >= b ? b : a; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MAX_L, "1" }) + public long testMaxL1E(long a, long b) { + return a >= b ? a : b; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MAX_L, "1" }) + public long testMaxL2E(long a, long b) { + return a <= b ? b : a; + } + + @Setup + static Object[] setupIntArrays() { + int[] a = new int[512]; + int[] b = new int[512]; + + for (int i = 0; i < 512; i++) { + a[i] = RANDOM.nextInt(); + b[i] = RANDOM.nextInt(); + } + + return new Object[] { a, b }; + } + + @Setup + static Object[] setupLongArrays() { + long[] a = new long[512]; + long[] b = new long[512]; + + for (int i = 0; i < 512; i++) { + a[i] = RANDOM.nextLong(); + b[i] = RANDOM.nextLong(); + } + + return new Object[] { a, b }; + } + + @Test + @IR(applyIf = { "SuperWordReductions", "true" }, + applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + counts = { IRNode.MAX_REDUCTION_V, "> 0" }) + @Arguments(setup = "setupIntArrays") + public Object[] testMaxIntReduction(int[] a, int[] b) { + int r = 0; + for (int i = 0; i < a.length; i++) { + int aI = a[i] * 2; + + r = aI > r ? aI : r; + } + + return new Object[] { a, r }; + } + + @Check(test = "testMaxIntReduction") + public void checkTestMaxIntReduction(Object[] vals) { + int[] a = (int[]) vals[0]; + int testRet = (int) vals[1]; + + int r = 0; + for (int i = 0; i < a.length; i++) { + int aI = a[i] * 2; + + r = aI > r ? aI : r; + } + + if (r != testRet) { + throw new IllegalStateException("Int max reduction test failed: expected " + testRet + " but got " + r); + } + } + + @Test + @IR(applyIf = { "SuperWordReductions", "true" }, + applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + counts = { IRNode.MIN_REDUCTION_V, "> 0" }) + @Arguments(setup = "setupIntArrays") + public Object[] testMinIntReduction(int[] a, int[] b) { + int r = 0; + + for (int i = 0; i < a.length; i++) { + int aI = a[i] * 2; + + r = aI < r ? aI : r; + } + + return new Object[] { a, r }; + } + + @Check(test = "testMinIntReduction") + public void checkTestMinIntReduction(Object[] vals) { + int[] a = (int[]) vals[0]; + int testRet = (int) vals[1]; + + int r = 0; + for (int i = 0; i < a.length; i++) { + int aI = a[i] * 2; + + r = aI < r ? aI : r; + } + + if (r != testRet) { + throw new IllegalStateException("Int min reduction test failed: expected " + testRet + " but got " + r); + } + } + + @Test + @IR(applyIf = { "SuperWordReductions", "true" }, + applyIfCPUFeatureOr = { "avx512", "true" }, + counts = { IRNode.MAX_REDUCTION_V, "> 0" }) + @Arguments(setup = "setupLongArrays") + public Object[] testMaxLongReduction(long[] a, long[] b) { + long r = 0; + + for (int i = 0; i < a.length; i++) { + long aI = a[i] * 2; + + r = aI > r ? aI : r; + } + + return new Object[] { a, r }; + } + + @Check(test = "testMaxLongReduction") + public void checkTestMaxLongReduction(Object[] vals) { + long[] a = (long[]) vals[0]; + long testRet = (long) vals[1]; + + long r = 0; + for (int i = 0; i < a.length; i++) { + long aI = a[i] * 2; + + r = aI > r ? aI : r; + } + + if (r != testRet) { + throw new IllegalStateException("Long max reduction test failed: expected " + testRet + " but got " + r); + } + } + + @Test + @IR(applyIf = { "SuperWordReductions", "true" }, + applyIfCPUFeatureOr = { "avx512", "true" }, + counts = { IRNode.MIN_REDUCTION_V, "> 0" }) + @Arguments(setup = "setupLongArrays") + public Object[] testMinLongReduction(long[] a, long[] b) { + long r = 0; + + for (int i = 0; i < a.length; i++) { + long aI = a[i] * 2; + + r = aI < r ? aI : r; + } + + return new Object[] { a, r }; + } + + @Check(test = "testMinLongReduction") + public void checkTestMinLongReduction(Object[] vals) { + long[] a = (long[]) vals[0]; + long testRet = (long) vals[1]; + + long r = 0; + for (int i = 0; i < a.length; i++) { + long aI = a[i] * 2; + + r = aI < r ? aI : r; + } + + if (r != testRet) { + throw new IllegalStateException("Long min reduction test failed: expected " + testRet + " but got " + r); + } + } + + @Test + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + counts = { IRNode.MAX_VI, "> 0" }) + @Arguments(setup = "setupIntArrays") + public Object[] testMaxIntVector(int[] a, int[] b) { + int[] r = new int[a.length]; + + for (int i = 0; i < a.length; i++) { + int aI = a[i]; + int bI = b[i]; + + r[i] = aI > bI ? aI : bI; + } + + return new Object[] { a, b, r }; + } + + @Check(test = "testMaxIntVector") + public void checkTestMaxIntVector(Object[] vals) { + int[] a = (int[]) vals[0]; + int[] b = (int[]) vals[1]; + int[] testRet = (int[]) vals[2]; + + for (int i = 0; i < a.length; i++) { + int aI = a[i]; + int bI = b[i]; + + int r = aI > bI ? aI : bI; + + if (r != testRet[i]) { + throw new IllegalStateException("Int max vectorization test failed: expected " + testRet + " but got " + r); + } + } + } + + @Test + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + counts = { IRNode.MIN_VI, "> 0" }) + @Arguments(setup = "setupIntArrays") + public Object[] testMinIntVector(int[] a, int[] b) { + int[] r = new int[a.length]; + + for (int i = 0; i < a.length; i++) { + int aI = a[i]; + int bI = b[i]; + + r[i] = aI < bI ? aI : bI; + } + + return new Object[] { a, b, r }; + } + + @Check(test = "testMinIntVector") + public void checkTestMinIntVector(Object[] vals) { + int[] a = (int[]) vals[0]; + int[] b = (int[]) vals[1]; + int[] testRet = (int[]) vals[2]; + + for (int i = 0; i < a.length; i++) { + int aI = a[i]; + int bI = b[i]; + + int r = aI < bI ? aI : bI; + + if (r != testRet[i]) { + throw new IllegalStateException("Int min vectorization test failed: expected " + testRet + " but got " + r); + } + } + } + + @Test + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + counts = { IRNode.MAX_VL, "> 0" }) + @Arguments(setup = "setupLongArrays") + public Object[] testMaxLongVector(long[] a, long[] b) { + long[] r = new long[a.length]; + + for (int i = 0; i < a.length; i++) { + long aI = a[i]; + long bI = b[i]; + + r[i] = aI > bI ? aI : bI; + } + + return new Object[] { a, b, r }; + } + + @Check(test = "testMaxLongVector") + public void checkTestMaxLongVector(Object[] vals) { + long[] a = (long[]) vals[0]; + long[] b = (long[]) vals[1]; + long[] testRet = (long[]) vals[2]; + + for (int i = 0; i < a.length; i++) { + long aI = a[i]; + long bI = b[i]; + + long r = aI > bI ? aI : bI; + + if (r != testRet[i]) { + throw new IllegalStateException("Long max vectorization test failed: expected " + testRet + " but got " + r); + } + } + } + + @Test + @IR(applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"}, + counts = { IRNode.MIN_VL, "> 0" }) + @Arguments(setup = "setupLongArrays") + public Object[] testMinLongVector(long[] a, long[] b) { + long[] r = new long[a.length]; + + for (int i = 0; i < a.length; i++) { + long aI = a[i]; + long bI = b[i]; + + r[i] = aI < bI ? aI : bI; + } + + return new Object[] { a, b, r }; + } + + @Check(test = "testMinLongVector") + public void checkTestMinLongVector(Object[] vals) { + long[] a = (long[]) vals[0]; + long[] b = (long[]) vals[1]; + long[] testRet = (long[]) vals[2]; + + for (int i = 0; i < a.length; i++) { + long aI = a[i]; + long bI = b[i]; + + long r = aI < bI ? aI : bI; + + if (r != testRet[i]) { + throw new IllegalStateException("Long min vectorization test failed: expected " + testRet + " but got " + r); + } + } + } + + @Run(test = { "testMinI1", "testMinI2", "testMaxI1", "testMaxI2", "testMinI1E", "testMinI2E", "testMaxI1E", "testMaxI2E" }) + public void runTestIntegers() { + testIntegers(10, 20); + testIntegers(20, 10); + testIntegers(10, 10); + testIntegers(Integer.MAX_VALUE, Integer.MIN_VALUE); + testIntegers(Integer.MIN_VALUE, Integer.MAX_VALUE); + testIntegers(RANDOM.nextInt(), RANDOM.nextInt()); + } + + @DontCompile + public void testIntegers(int a, int b) { + Asserts.assertEQ(a < b ? a : b, testMinI1(a, b)); + Asserts.assertEQ(a > b ? b : a, testMinI2(a, b)); + Asserts.assertEQ(a > b ? a : b, testMaxI1(a, b)); + Asserts.assertEQ(a < b ? b : a, testMaxI2(a, b)); + + Asserts.assertEQ(a <= b ? a : b, testMinI1E(a, b)); + Asserts.assertEQ(a >= b ? b : a, testMinI2E(a, b)); + Asserts.assertEQ(a >= b ? a : b, testMaxI1E(a, b)); + Asserts.assertEQ(a <= b ? b : a, testMaxI2E(a, b)); + } + + @Run(test = { "testMinL1", "testMinL2", "testMaxL1", "testMaxL2", "testMinL1E", "testMinL2E", "testMaxL1E", "testMaxL2E" }) + public void runTestLongs() { + testLongs(10, 20); + testLongs(20, 10); + testLongs(10, 10); + testLongs(Integer.MAX_VALUE, Integer.MIN_VALUE); + testLongs(Integer.MIN_VALUE, Integer.MAX_VALUE); + testLongs(Long.MAX_VALUE, Long.MIN_VALUE); + testLongs(Long.MIN_VALUE, Long.MAX_VALUE); + testLongs(RANDOM.nextLong(), RANDOM.nextLong()); + } + + @DontCompile + public void testLongs(long a, long b) { + Asserts.assertEQ(a < b ? a : b, testMinL1(a, b)); + Asserts.assertEQ(a > b ? b : a, testMinL2(a, b)); + Asserts.assertEQ(a > b ? a : b, testMaxL1(a, b)); + Asserts.assertEQ(a < b ? b : a, testMaxL2(a, b)); + + Asserts.assertEQ(a <= b ? a : b, testMinL1E(a, b)); + Asserts.assertEQ(a >= b ? b : a, testMinL2E(a, b)); + Asserts.assertEQ(a >= b ? a : b, testMaxL1E(a, b)); + Asserts.assertEQ(a <= b ? b : a, testMaxL2E(a, b)); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 3fd0455da1d..b9a6bcccf12 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -867,6 +867,11 @@ public class IRNode { vectorNode(MAX_VD, "MaxV", TYPE_DOUBLE); } + public static final String MAX_VL = VECTOR_PREFIX + "MAX_VL" + POSTFIX; + static { + vectorNode(MAX_VL, "MaxV", TYPE_LONG); + } + public static final String MEMBAR = PREFIX + "MEMBAR" + POSTFIX; static { beforeMatchingNameRegex(MEMBAR, "MemBar"); @@ -927,6 +932,11 @@ public class IRNode { vectorNode(MIN_VD, "MinV", TYPE_DOUBLE); } + public static final String MIN_VL = VECTOR_PREFIX + "MIN_VL" + POSTFIX; + static { + vectorNode(MIN_VL, "MinV", TYPE_LONG); + } + public static final String MUL = PREFIX + "MUL" + POSTFIX; static { beforeMatchingNameRegex(MUL, "Mul(I|L|F|D)"); diff --git a/test/micro/org/openjdk/bench/vm/compiler/IfMinMax.java b/test/micro/org/openjdk/bench/vm/compiler/IfMinMax.java new file mode 100644 index 00000000000..adb09056b92 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/IfMinMax.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, 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.vm.compiler; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Fork(3) +public class IfMinMax { + private static final int SIZE = 10_000; + + @Benchmark + public void testSingleInt(Blackhole blackhole, BenchState state) { + int a = state.i1[state.random.nextInt(SIZE)]; + int b = state.i2[state.random.nextInt(SIZE)]; + blackhole.consume(a > b ? a : b); + } + + @Benchmark + public void testVectorInt(Blackhole blackhole, BenchState state) { + for (int i = 0; i < SIZE; i++) { + state.i3[i] = state.i1[i] > state.i2[i] ? state.i1[i] : state.i2[i]; + } + + blackhole.consume(state.i3); + } + + @Benchmark + public void testReductionInt(Blackhole blackhole, BenchState state) { + int a = 0; + + for (int i = 0; i < SIZE; i++) { + if (state.i1[i] > a) { + a = state.i1[i]; + } + } + + blackhole.consume(a); + } + + @Benchmark + public void testSingleLong(Blackhole blackhole, BenchState state) { + long a = state.l1[state.random.nextInt(SIZE)]; + long b = state.l2[state.random.nextInt(SIZE)]; + blackhole.consume(a > b ? a : b); + } + + @Benchmark + public void testVectorLong(Blackhole blackhole, BenchState state) { + for (int i = 0; i < SIZE; i++) { + state.l3[i] = state.l1[i] > state.l2[i] ? state.l1[i] : state.l2[i]; + } + + blackhole.consume(state.l3); + } + + @Benchmark + public void testReductionLong(Blackhole blackhole, BenchState state) { + long a = 0; + + for (int i = 0; i < SIZE; i++) { + if (state.l1[i] > a) { + a = state.l1[i]; + } + } + + blackhole.consume(a); + } + + @State(Scope.Benchmark) + public static class BenchState { + private final int[] i1 = new int[SIZE]; + private final int[] i2 = new int[SIZE]; + private final int[] i3 = new int[SIZE]; + + private final long[] l1 = new long[SIZE]; + private final long[] l2 = new long[SIZE]; + private final long[] l3 = new long[SIZE]; + + private Random random; + + public BenchState() { + } + + @Setup + public void setup() { + this.random = new Random(1000); + + for (int i = 0; i < SIZE; i++) { + i1[i] = this.random.nextInt(); + i2[i] = this.random.nextInt(); + i3[i] = this.random.nextInt(); + + l1[i] = this.random.nextLong(); + l2[i] = this.random.nextLong(); + l3[i] = this.random.nextLong(); + } + } + } +} \ No newline at end of file