From 140f56718bbbfc31bb0c39255c68568fad285a1f Mon Sep 17 00:00:00 2001 From: Joshua Cao Date: Mon, 15 Apr 2024 15:54:34 +0000 Subject: [PATCH] 8323220: Reassociate loop invariants involved in Cmps and Add/Subs Reviewed-by: epeter, xliu, chagedorn --- src/hotspot/share/opto/loopTransform.cpp | 72 ++- src/hotspot/share/opto/loopnode.hpp | 6 +- .../InvariantCodeMotionReassociateAddSub.java | 525 ++++++++++++++++++ .../InvariantCodeMotionReassociateCmp.java | 412 ++++++++++++++ 4 files changed, 994 insertions(+), 21 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java create mode 100644 test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateCmp.java diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 5fd98251af5..21e8807d15d 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -254,11 +254,10 @@ void IdealLoopTree::compute_profile_trip_cnt(PhaseIdealLoop *phase) { head->set_profile_trip_cnt(trip_cnt); } -//---------------------find_invariant----------------------------- // Return nonzero index of invariant operand for an associative // binary operation of (nonconstant) invariant and variant values. // Helper for reassociate_invariants. -int IdealLoopTree::find_invariant(Node* n, PhaseIdealLoop *phase) { +int IdealLoopTree::find_invariant(Node* n, PhaseIdealLoop* phase) { bool in1_invar = this->is_invariant(n->in(1)); bool in2_invar = this->is_invariant(n->in(2)); if (in1_invar && !in2_invar) return 1; @@ -266,7 +265,24 @@ int IdealLoopTree::find_invariant(Node* n, PhaseIdealLoop *phase) { return 0; } -//---------------------is_associative----------------------------- +// Return TRUE if "n" is an associative cmp node. A cmp node is +// associative if it is only used for equals or not-equals +// comparisons of integers or longs. We cannot reassociate +// non-equality comparisons due to possibility of overflow. +bool IdealLoopTree::is_associative_cmp(Node* n) { + if (n->Opcode() != Op_CmpI && n->Opcode() != Op_CmpL) { + return false; + } + for (DUIterator i = n->outs(); n->has_out(i); i++) { + BoolNode* bool_out = n->out(i)->isa_Bool(); + if (bool_out == nullptr || !(bool_out->_test._test == BoolTest::eq || + bool_out->_test._test == BoolTest::ne)) { + return false; + } + } + return true; +} + // Return TRUE if "n" is an associative binary node. If "base" is // not null, "n" must be re-associative with it. bool IdealLoopTree::is_associative(Node* n, Node* base) { @@ -274,25 +290,26 @@ bool IdealLoopTree::is_associative(Node* n, Node* base) { if (base != nullptr) { assert(is_associative(base), "Base node should be associative"); int base_op = base->Opcode(); - if (base_op == Op_AddI || base_op == Op_SubI) { + if (base_op == Op_AddI || base_op == Op_SubI || base_op == Op_CmpI) { return op == Op_AddI || op == Op_SubI; } - if (base_op == Op_AddL || base_op == Op_SubL) { + if (base_op == Op_AddL || base_op == Op_SubL || base_op == Op_CmpL) { return op == Op_AddL || op == Op_SubL; } return op == base_op; } else { - // Integer "add/sub/mul/and/or/xor" operations are associative. + // Integer "add/sub/mul/and/or/xor" operations are associative. Integer + // "cmp" operations are associative if it is an equality comparison. return op == Op_AddI || op == Op_AddL || op == Op_SubI || op == Op_SubL || op == Op_MulI || op == Op_MulL || op == Op_AndI || op == Op_AndL || op == Op_OrI || op == Op_OrL - || op == Op_XorI || op == Op_XorL; + || op == Op_XorI || op == Op_XorL + || is_associative_cmp(n); } } -//---------------------reassociate_add_sub------------------------ // Reassociate invariant add and subtract expressions: // // inv1 + (x + inv2) => ( inv1 + inv2) + x @@ -308,22 +325,32 @@ bool IdealLoopTree::is_associative(Node* n, Node* base) { // (inv2 - x) - inv1 => (-inv1 + inv2) - x // inv1 - (x + inv2) => ( inv1 - inv2) - x // -Node* IdealLoopTree::reassociate_add_sub(Node* n1, int inv1_idx, int inv2_idx, PhaseIdealLoop *phase) { - assert(n1->is_Add() || n1->is_Sub(), "Target node should be add or subtract"); +// Apply the same transformations to == and != +// inv1 == (x + inv2) => ( inv1 - inv2 ) == x +// inv1 == (x - inv2) => ( inv1 + inv2 ) == x +// inv1 == (inv2 - x) => (-inv1 + inv2 ) == x +Node* IdealLoopTree::reassociate_add_sub_cmp(Node* n1, int inv1_idx, int inv2_idx, PhaseIdealLoop* phase) { Node* n2 = n1->in(3 - inv1_idx); + bool n1_is_sub = n1->is_Sub() && !n1->is_Cmp(); + bool n1_is_cmp = n1->is_Cmp(); + bool n2_is_sub = n2->is_Sub(); + assert(n1->is_Add() || n1_is_sub || n1_is_cmp, "Target node should be add, subtract, or compare"); + assert(n2->is_Add() || (n2_is_sub && !n2->is_Cmp()), "Child node should be add or subtract"); Node* inv1 = n1->in(inv1_idx); Node* inv2 = n2->in(inv2_idx); Node* x = n2->in(3 - inv2_idx); - bool neg_x = n2->is_Sub() && inv2_idx == 1; - bool neg_inv2 = n2->is_Sub() && inv2_idx == 2; - bool neg_inv1 = n1->is_Sub() && inv1_idx == 2; - if (n1->is_Sub() && inv1_idx == 1) { + // Determine whether x, inv1, or inv2 should be negative in the transformed + // expression + bool neg_x = n2_is_sub && inv2_idx == 1; + bool neg_inv2 = (n2_is_sub && !n1_is_cmp && inv2_idx == 2) || (n1_is_cmp && !n2_is_sub); + bool neg_inv1 = (n1_is_sub && inv1_idx == 2) || (n1_is_cmp && inv2_idx == 1 && n2_is_sub); + if (n1_is_sub && inv1_idx == 1) { neg_x = !neg_x; neg_inv2 = !neg_inv2; } - bool is_int = n1->bottom_type()->isa_int() != nullptr; + bool is_int = n2->bottom_type()->isa_int() != nullptr; Node* inv1_c = phase->get_ctrl(inv1); Node* n_inv1; if (neg_inv1) { @@ -349,6 +376,9 @@ Node* IdealLoopTree::reassociate_add_sub(Node* n1, int inv1_idx, int inv2_idx, P inv = new AddINode(n_inv1, inv2); } phase->register_new_node(inv, phase->get_early_ctrl(inv)); + if (n1_is_cmp) { + return new CmpINode(x, inv); + } if (neg_x) { return new SubINode(inv, x); } else { @@ -361,6 +391,9 @@ Node* IdealLoopTree::reassociate_add_sub(Node* n1, int inv1_idx, int inv2_idx, P inv = new AddLNode(n_inv1, inv2); } phase->register_new_node(inv, phase->get_early_ctrl(inv)); + if (n1_is_cmp) { + return new CmpLNode(x, inv); + } if (neg_x) { return new SubLNode(inv, x); } else { @@ -369,10 +402,9 @@ Node* IdealLoopTree::reassociate_add_sub(Node* n1, int inv1_idx, int inv2_idx, P } } -//---------------------reassociate----------------------------- // Reassociate invariant binary expressions with add/sub/mul/ -// and/or/xor operators. -// For add/sub expressions: see "reassociate_add_sub" +// and/or/xor/cmp operators. +// For add/sub/cmp expressions: see "reassociate_add_sub_cmp" // // For mul/and/or/xor expressions: // @@ -399,7 +431,9 @@ Node* IdealLoopTree::reassociate(Node* n1, PhaseIdealLoop *phase) { case Op_AddL: case Op_SubI: case Op_SubL: - result = reassociate_add_sub(n1, inv1_idx, inv2_idx, phase); + case Op_CmpI: + case Op_CmpL: + result = reassociate_add_sub_cmp(n1, inv1_idx, inv2_idx, phase); break; case Op_MulI: case Op_MulL: diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index e4654f6538c..389e381e542 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -739,13 +739,15 @@ public: void reassociate_invariants(PhaseIdealLoop *phase); // Reassociate invariant binary expressions. Node* reassociate(Node* n1, PhaseIdealLoop *phase); - // Reassociate invariant add and subtract expressions. - Node* reassociate_add_sub(Node* n1, int inv1_idx, int inv2_idx, PhaseIdealLoop *phase); + // Reassociate invariant add, subtract, and compare expressions. + Node* reassociate_add_sub_cmp(Node* n1, int inv1_idx, int inv2_idx, PhaseIdealLoop* phase); // Return nonzero index of invariant operand if invariant and variant // are combined with an associative binary. Helper for reassociate_invariants. int find_invariant(Node* n, PhaseIdealLoop *phase); // Return TRUE if "n" is associative. bool is_associative(Node* n, Node* base=nullptr); + // Return TRUE if "n" is an associative cmp node. + bool is_associative_cmp(Node* n); // Return true if n is invariant bool is_invariant(Node* n) const; diff --git a/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java new file mode 100644 index 00000000000..4e8daf3d163 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java @@ -0,0 +1,525 @@ +/* + * Copyright Amazon.com Inc. 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.loopopts; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8323220 + * @summary Test loop invariant code motion of add/sub through reassociation + * @library /test/lib / + * @run driver compiler.c2.loopopts.InvariantCodeMotionReassociateAddSub + */ +public class InvariantCodeMotionReassociateAddSub { + private static final Random RANDOM = Utils.getRandomInstance(); + private int size; + private int inv1; + private int inv2; + + public static void main(String[] args) { + TestFramework.run(); + } + + @DontInline + private int blackhole(int i) { return i; } + @DontInline + private int blackhole(long i) { return (int)i; } + + @Setup + public Object[] setup(SetupInfo info) { + int count = info.invocationCounter(); + size = count + 500; + inv1 = count; + if (RANDOM.nextInt() % 7 == 0) { + // Setup inputs to be equals sometimes to avoid uncommon traps + inv2 = inv1; + } else { + inv2 = count * 2; + } + return new Object[] { inv1, inv2, size }; + } + + public void fail(int returnValue) { + throw new RuntimeException("Illegal reassociation: returnValue=" + returnValue + ", inv1=" + inv1 + + ", inv2=" + inv2 + ", size=" + size); + } + + public void check(int returnValue, int expected) { + if (returnValue != expected) { + fail(returnValue); + } + } + + public void checkAdd(int returnValue) { + check(returnValue, inv1 + inv2 + size - 1); + } + + public void checkSubAdd(int returnValue) { + check(returnValue, inv1 - inv2 + size - 1); + } + + public void checkNegSubAdd(int returnValue) { + check(returnValue, -inv1 - inv2 + size - 1); + } + + public void checkAddSub(int returnValue) { + check(returnValue, inv1 + inv2 - (size - 1)); + } + + public void checkSubSub(int returnValue) { + check(returnValue, inv1 - inv2 - (size - 1)); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "3"}) + public int addInt(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 + i` + result = blackhole(inv1 + i + inv2); + } + return result; + } + + @Check(test = "addInt") + public void checkAddInt(int returnValue) { + checkAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "2"}) + public int addLong(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 + i` + result = blackhole(inv1 + i + inv2); + } + return result; + } + + @Check(test = "addLong") + public void checkAddLong(int returnValue) { + checkAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "3"}) + public int addInt2(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 + i` + result = blackhole(inv1 + (i + inv2)); + } + return result; + } + + @Check(test = "addInt2") + public void checkAddInt2(int returnValue) { + checkAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "2"}) + public int addLong2(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 + i` + result = blackhole(inv1 + (i + inv2)); + } + return result; + } + + @Check(test = "addLong2") + public void checkAddLong2(int returnValue) { + checkAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int minusAddInt(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 + i` + result = blackhole(inv1 + (i - inv2)); + } + return result; + } + + @Check(test = "minusAddInt") + public void checkSubAddInt(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int minusAddLong(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 + i` + result = blackhole(inv1 + (i - inv2)); + } + return result; + } + + @Check(test = "minusAddLong") + public void checkSubAddLong(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int minusAddInt2(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 + i` + result = blackhole(inv1 - (inv2 - i)); + } + return result; + } + + @Check(test = "minusAddInt2") + public void checkSubAddInt2(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int minusAddLong2(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 + i` + result = blackhole(inv1 - (inv2 - i)); + } + return result; + } + + @Check(test = "minusAddLong2") + public void checkSubAddLong2(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int minusAddInt3(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 + i` + result = blackhole(i - inv2 + inv1); + } + return result; + } + + @Check(test = "minusAddInt3") + public void checkSubAddInt3(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int minusAddLong3(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 + i` + result = blackhole(i - inv2 + inv1); + } + return result; + } + + @Check(test = "minusAddLong3") + public void checkSubAddLong3(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int negAddInt(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `-inv2 + inv1 + i` + result = blackhole(i + inv1 - inv2); + } + return result; + } + + @Check(test = "negAddInt") + public void checkNegAddInt(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int negAddLong(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `-inv2 + inv1 + i` + result = blackhole(i + inv1 - inv2); + } + return result; + } + + @Check(test = "negAddLong") + public void checkNegAddLong(int returnValue) { + checkSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "2"}) + public int negSubAddInt(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `-inv1 - inv2 + i` + result = blackhole(i - inv1 - inv2); + } + return result; + } + + @Check(test = "negSubAddInt") + public void checkNegSubAddInt(int returnValue) { + checkNegSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "2"}) + public int negSubAddLong(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `-inv1 - inv2 + i` + result = blackhole(i - inv1 - inv2); + } + return result; + } + + @Check(test = "negSubAddLong") + public void checkNegSubAddLong(int returnValue) { + checkNegSubAdd(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int addSubInt(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 - i` + result = blackhole(inv1 + (inv2 - i)); + } + return result; + } + + @Check(test = "addSubInt") + public void checkAddSubInt(int returnValue) { + checkAddSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int addSubLong(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 - i` + result = blackhole(inv1 + (inv2 - i)); + } + return result; + } + + @Check(test = "addSubLong") + public void checkAddSubLong(int returnValue) { + checkAddSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int addSubInt2(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 - i` + result = blackhole(inv1 - (i - inv2)); + } + return result; + } + + @Check(test = "addSubInt2") + public void checkAddSubInt2(int returnValue) { + checkAddSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int addSubLong2(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 - i` + result = blackhole(inv1 - (i - inv2)); + } + return result; + } + + @Check(test = "addSubLong2") + public void checkAddSubLong2(int returnValue) { + checkAddSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.SUB_I, "1"}) + public int addSubInt3(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 - i` + result = blackhole(inv2 - i + inv1); + } + return result; + } + + @Check(test = "addSubInt3") + public void checkAddSubInt3(int returnValue) { + checkAddSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_L, "1"}) + @IR(counts = {IRNode.SUB_L, "1"}) + public int addSubLong3(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 + inv2 - i` + result = blackhole(inv2 - i + inv1); + } + return result; + } + + @Check(test = "addSubLong3") + public void checkAddSubLong3(int returnValue) { + checkAddSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "2"}) + public int subSubInt(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 - i` + result = blackhole(inv1 - (i + inv2)); + } + return result; + } + + @Check(test = "subSubInt") + public void checkSubSubInt(int returnValue) { + checkSubSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = {IRNode.ADD_L}) + @IR(counts = {IRNode.SUB_L, "2"}) + public int subSubLong(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 - i` + result = blackhole(inv1 - (i + inv2)); + } + return result; + } + + @Check(test = "subSubLong") + public void checkSubSubLong(int returnValue) { + checkSubSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.SUB_I, "2"}) + public int subSubInt2(int inv1, int inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 - i` + result = blackhole(inv1 - i - inv2); + } + return result; + } + + @Check(test = "subSubInt2") + public void checkSubSubInt2(int returnValue) { + checkSubSub(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = {IRNode.ADD_L}) + @IR(counts = {IRNode.SUB_L, "2"}) + public int subSubLong2(long inv1, long inv2, int size) { + int result = -1; + for (int i = 0; i < size; ++i) { + // Reassociate to `inv1 - inv2 - i` + result = blackhole(inv1 - i - inv2); + } + return result; + } + + @Check(test = "subSubLong2") + public void checkSubSubLong2(int returnValue) { + checkSubSub(returnValue); + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateCmp.java b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateCmp.java new file mode 100644 index 00000000000..566e294b4bf --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateCmp.java @@ -0,0 +1,412 @@ +/* + * Copyright Amazon.com Inc. 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.loopopts; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8323220 + * @summary Test loop invariant code motion for cmp nodes through reassociation + * @library /test/lib / + * @run driver compiler.c2.loopopts.InvariantCodeMotionReassociateCmp + */ +public class InvariantCodeMotionReassociateCmp { + private static final Random RANDOM = Utils.getRandomInstance(); + private int size; + private int inv1; + private int inv2; + + public static void main(String[] args) { + TestFramework.run(); + } + + @DontInline + private void blackhole() {} + + @Setup + public Object[] setup(SetupInfo info) { + int count = info.invocationCounter(); + size = count + 500; + inv1 = count; + if (RANDOM.nextInt() % 7 == 0) { + // Setup inputs to be equals sometimes to avoid uncommon traps + inv2 = inv1; + } else { + inv2 = count * 2; + } + return new Object[] { inv1, inv2, size }; + } + + public void fail(int returnValue) { + throw new RuntimeException("Illegal reassociation: returnValue=" + returnValue + ", inv1=" + inv1 + + ", inv2=" + inv2 + ", size=" + size); + } + + public void checkEq(int returnValue) { + int invDiff = inv2 - inv1; + if ((invDiff < size && returnValue != invDiff) || (invDiff >= size && returnValue != size)) { + fail(returnValue); + } + } + + public void checkNe(int returnValue) { + int invDiff = inv2 - inv1; + if ((invDiff != 0 && returnValue != 0) || (invDiff == 0 && returnValue != 1)) { + fail(returnValue); + } + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_I, "1"}) + public int equalsAddInt(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv2 - inv1 == i` + if (inv1 + i == inv2) { + break; + } + } + return i; + } + + @Check(test = "equalsAddInt") + public void checkEqualsAddInt(int returnValue) { + checkEq(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_L, "1"}) + public int equalsAddLong(long inv1, long inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv2 - inv1 == i` + if (inv1 + i == inv2) { + break; + } + } + return i; + } + + @Check(test = "equalsAddLong") + public void checkEqualsAddLong(int returnValue) { + checkEq(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_I, "1"}) + public int equalsInvariantSubVariantInt(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv1 - inv2 == i` + if (inv2 - i == inv1) { + break; + } + } + return i; + } + + @Check(test = "equalsInvariantSubVariantInt") + public void checkEqualsInvariantSubVariantInt(int returnValue) { + checkEq(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_L, "1"}) + public int equalsInvariantSubVariantLong(long inv1, long inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv2 - inv1 == i` + if (inv2 - i == inv1) { + break; + } + } + return i; + } + + @Check(test = "equalsInvariantSubVariantLong") + public void checkEqualsInvariantSubVariantLong(int returnValue) { + checkEq(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_I, "1"}) + public int equalsVariantSubInvariantInt(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv2 - inv1 == i` + if (i - inv2 == -inv1) { + break; + } + } + return i; + } + + @Check(test = "equalsVariantSubInvariantInt") + public void checkEqualsVariantSubInvariantInt(int returnValue) { + checkEq(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_L, "1"}) + public int equalsVariantSubInvariantLong(long inv1, long inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv2 - inv1 == i` + if (i - inv2 == -inv1) { + break; + } + } + return i; + } + + @Check(test = "equalsVariantSubInvariantLong") + public void checkEqualsVariantSubInvariantLong(int returnValue) { + checkEq(returnValue); + } + + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_I, "1"}) + public int notEqualsAddInt(int inv1, int inv2, int size) { + int i = 0; + for (; i < 500; ++i) { + blackhole(); + // Reassociate to `inv1 - inv2 != i` + if (inv1 + i != inv2) { + break; + } + } + return i; + } + + @Check(test = "notEqualsAddInt") + public void checkNotEqualsAddInt(int returnValue) { + checkNe(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_L, "1"}) + public int notEqualsAddLong(long inv1, long inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv1 - inv2 != i` + if (inv1 + i != inv2) { + break; + } + } + return i; + } + + @Check(test = "notEqualsAddLong") + public void checkNotEqualsAddLong(int returnValue) { + checkNe(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_I, "1"}) + public int notEqualsInvariantSubVariantInt(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv1 - inv2 != i` + if (inv1 - i != inv2) { + break; + } + } + return i; + } + + @Check(test = "notEqualsInvariantSubVariantInt") + public void checkNotEqualsInvariantSubVariantInt(int returnValue) { + checkNe(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_L, "1"}) + public int notEqualsInvariantSubVariantLong(long inv1, long inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv1 - inv2 != i` + if (inv1 - i != inv2) { + break; + } + } + return i; + } + + @Check(test = "notEqualsInvariantSubVariantLong") + public void checkNotEqualsInvariantSubVariantLong(int returnValue) { + checkNe(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_I, "1"}) + public int notEqualsVariantSubInvariantInt(int inv1, int inv2, int size) { + int i = 0; + for (; i < 500; ++i) { + blackhole(); + // Reassociate to `inv2 - inv1 != i` + if (i - inv2 != -inv1) { + break; + } + } + return i; + } + + @Check(test = "notEqualsVariantSubInvariantInt") + public void checkNotEqualsVariantSubInvariantInt(int returnValue) { + checkNe(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(counts = {IRNode.SUB_L, "1"}) + public int notEqualsVariantSubInvariantLong(long inv1, long inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + // Reassociate to `inv1 - inv1 != i` + if (i - inv2 != -inv1) { + break; + } + } + return i; + } + + @Check(test = "notEqualsVariantSubInvariantLong") + public void checkNotEqualsVariantSubInvariantLong(int returnValue) { + checkNe(returnValue); + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = {IRNode.SUB_I}) + public int ltDontReassociate(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + if (inv1 + i < inv2) { + break; + } + } + return i; + } + + @Check(test = "ltDontReassociate") + public void checkLtDontReassociate(int returnValue) { + int sum = inv1 + returnValue; + if ((returnValue < size && sum >= inv2) || (returnValue > size && sum < inv2)) { + fail(returnValue); + } + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = {IRNode.SUB_I}) + public int leDontReassociate(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + if (inv1 + i <= inv2) { + break; + } + } + return i; + } + + @Check(test = "leDontReassociate") + public void checkLeDontReassociate(int returnValue) { + int sum = inv1 + returnValue; + if ((returnValue < size && sum > inv2) || (returnValue > size && sum <= inv2)) { + fail(returnValue); + } + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = {IRNode.SUB_I}) + public int gtDontReassociate(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + if (inv1 + i > inv2) { + break; + } + } + return i; + } + + @Check(test = "gtDontReassociate") + public void checkGtDontReassociate(int returnValue) { + int sum = inv1 + returnValue; + if ((returnValue < size && sum <= inv2) || (returnValue > size && sum > inv2)) { + fail(returnValue); + } + } + + @Test + @Arguments(setup = "setup") + @IR(failOn = {IRNode.SUB_I}) + public int geDontReassociate(int inv1, int inv2, int size) { + int i = 0; + for (; i < size; ++i) { + blackhole(); + if (inv1 + i >= inv2) { + break; + } + } + return i; + } + + @Check(test = "geDontReassociate") + public void checkGeDontReassociate(int returnValue) { + int sum = inv1 + returnValue; + if ((returnValue < size && sum < inv2) || (returnValue > size && sum >= inv2)) { + fail(returnValue); + } + } +}