8302673: [SuperWord] MaxReduction and MinReduction should vectorize for int
Co-authored-by: Jatin Bhateja <jbhateja@openjdk.org> Reviewed-by: epeter, kvn
This commit is contained in:
parent
22a9a86be0
commit
3fa776d66a
@ -1030,6 +1030,14 @@ const Type* XorLNode::Value(PhaseGVN* phase) const {
|
|||||||
return AddNode::Value(phase);
|
return AddNode::Value(phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* build_min_max_int(Node* a, Node* b, bool is_max) {
|
||||||
|
if (is_max) {
|
||||||
|
return new MaxINode(a, b);
|
||||||
|
} else {
|
||||||
|
return new MinINode(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn) {
|
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();
|
bool is_int = gvn.type(a)->isa_int();
|
||||||
assert(is_int || gvn.type(a)->isa_long(), "int or long inputs");
|
assert(is_int || gvn.type(a)->isa_long(), "int or long inputs");
|
||||||
@ -1044,13 +1052,7 @@ Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, co
|
|||||||
}
|
}
|
||||||
Node* res = nullptr;
|
Node* res = nullptr;
|
||||||
if (is_int && !is_unsigned) {
|
if (is_int && !is_unsigned) {
|
||||||
Node* res_new = nullptr;
|
res = gvn.transform(build_min_max_int(a, b, is_max));
|
||||||
if (is_max) {
|
|
||||||
res_new = new MaxINode(a, b);
|
|
||||||
} else {
|
|
||||||
res_new = new MinINode(a, b);
|
|
||||||
}
|
|
||||||
res = gvn.transform(res_new);
|
|
||||||
assert(gvn.type(res)->is_int()->_lo >= t->is_int()->_lo && gvn.type(res)->is_int()->_hi <= t->is_int()->_hi, "type doesn't match");
|
assert(gvn.type(res)->is_int()->_lo >= t->is_int()->_lo && gvn.type(res)->is_int()->_hi <= t->is_int()->_hi, "type doesn't match");
|
||||||
} else {
|
} else {
|
||||||
Node* cmp = nullptr;
|
Node* cmp = nullptr;
|
||||||
@ -1096,6 +1098,113 @@ Node* MaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if addition of an integer with type 't' and a constant 'c' can overflow.
|
||||||
|
static bool can_overflow(const TypeInt* t, jint c) {
|
||||||
|
jint t_lo = t->_lo;
|
||||||
|
jint t_hi = t->_hi;
|
||||||
|
return ((c < 0 && (java_add(t_lo, c) > t_lo)) ||
|
||||||
|
(c > 0 && (java_add(t_hi, c) < t_hi)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let <x, x_off> = x_operands and <y, y_off> = y_operands.
|
||||||
|
// If x == y and neither add(x, x_off) nor add(y, y_off) overflow, return
|
||||||
|
// add(x, op(x_off, y_off)). Otherwise, return nullptr.
|
||||||
|
Node* MaxNode::extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands) {
|
||||||
|
Node* x = x_operands.first;
|
||||||
|
Node* y = y_operands.first;
|
||||||
|
int opcode = Opcode();
|
||||||
|
assert(opcode == Op_MaxI || opcode == Op_MinI, "Unexpected opcode");
|
||||||
|
const TypeInt* tx = phase->type(x)->isa_int();
|
||||||
|
jint x_off = x_operands.second;
|
||||||
|
jint y_off = y_operands.second;
|
||||||
|
if (x == y && tx != nullptr &&
|
||||||
|
!can_overflow(tx, x_off) &&
|
||||||
|
!can_overflow(tx, y_off)) {
|
||||||
|
jint c = opcode == Op_MinI ? MIN2(x_off, y_off) : MAX2(x_off, y_off);
|
||||||
|
return new AddINode(x, phase->intcon(c));
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to cast n as an integer addition with a constant. Return:
|
||||||
|
// <x, C>, if n == add(x, C), where 'C' is a non-TOP constant;
|
||||||
|
// <nullptr, 0>, if n == add(x, C), where 'C' is a TOP constant; or
|
||||||
|
// <n, 0>, otherwise.
|
||||||
|
static ConstAddOperands as_add_with_constant(Node* n) {
|
||||||
|
if (n->Opcode() != Op_AddI) {
|
||||||
|
return ConstAddOperands(n, 0);
|
||||||
|
}
|
||||||
|
Node* x = n->in(1);
|
||||||
|
Node* c = n->in(2);
|
||||||
|
if (!c->is_Con()) {
|
||||||
|
return ConstAddOperands(n, 0);
|
||||||
|
}
|
||||||
|
const Type* c_type = c->bottom_type();
|
||||||
|
if (c_type == Type::TOP) {
|
||||||
|
return ConstAddOperands(nullptr, 0);
|
||||||
|
}
|
||||||
|
return ConstAddOperands(x, c_type->is_int()->get_con());
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* MaxNode::IdealI(PhaseGVN* phase, bool can_reshape) {
|
||||||
|
int opcode = Opcode();
|
||||||
|
assert(opcode == Op_MinI || opcode == Op_MaxI, "Unexpected opcode");
|
||||||
|
// Try to transform the following pattern, in any of its four possible
|
||||||
|
// permutations induced by op's commutativity:
|
||||||
|
// op(op(add(inner, inner_off), inner_other), add(outer, outer_off))
|
||||||
|
// into
|
||||||
|
// op(add(inner, op(inner_off, outer_off)), inner_other),
|
||||||
|
// where:
|
||||||
|
// op is either MinI or MaxI, and
|
||||||
|
// inner == outer, and
|
||||||
|
// the additions cannot overflow.
|
||||||
|
for (uint inner_op_index = 1; inner_op_index <= 2; inner_op_index++) {
|
||||||
|
if (in(inner_op_index)->Opcode() != opcode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Node* outer_add = in(inner_op_index == 1 ? 2 : 1);
|
||||||
|
ConstAddOperands outer_add_operands = as_add_with_constant(outer_add);
|
||||||
|
if (outer_add_operands.first == nullptr) {
|
||||||
|
return nullptr; // outer_add has a TOP input, no need to continue.
|
||||||
|
}
|
||||||
|
// One operand is a MinI/MaxI and the other is an integer addition with
|
||||||
|
// constant. Test the operands of the inner MinI/MaxI.
|
||||||
|
for (uint inner_add_index = 1; inner_add_index <= 2; inner_add_index++) {
|
||||||
|
Node* inner_op = in(inner_op_index);
|
||||||
|
Node* inner_add = inner_op->in(inner_add_index);
|
||||||
|
ConstAddOperands inner_add_operands = as_add_with_constant(inner_add);
|
||||||
|
if (inner_add_operands.first == nullptr) {
|
||||||
|
return nullptr; // inner_add has a TOP input, no need to continue.
|
||||||
|
}
|
||||||
|
// Try to extract the inner add.
|
||||||
|
Node* add_extracted = extract_add(phase, inner_add_operands, outer_add_operands);
|
||||||
|
if (add_extracted == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Node* add_transformed = phase->transform(add_extracted);
|
||||||
|
Node* inner_other = inner_op->in(inner_add_index == 1 ? 2 : 1);
|
||||||
|
return build_min_max_int(add_transformed, inner_other, opcode == Op_MaxI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try to transform
|
||||||
|
// op(add(x, x_off), add(y, y_off))
|
||||||
|
// into
|
||||||
|
// add(x, op(x_off, y_off)),
|
||||||
|
// where:
|
||||||
|
// op is either MinI or MaxI, and
|
||||||
|
// inner == outer, and
|
||||||
|
// the additions cannot overflow.
|
||||||
|
ConstAddOperands xC = as_add_with_constant(in(1));
|
||||||
|
ConstAddOperands yC = as_add_with_constant(in(2));
|
||||||
|
if (xC.first == nullptr || yC.first == nullptr) return nullptr;
|
||||||
|
return extract_add(phase, xC, yC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ideal transformations for MaxINode
|
||||||
|
Node* MaxINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||||
|
return IdealI(phase, can_reshape);
|
||||||
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
//------------------------------add_ring---------------------------------------
|
//------------------------------add_ring---------------------------------------
|
||||||
// Supplied function returns the sum of the inputs.
|
// Supplied function returns the sum of the inputs.
|
||||||
@ -1107,174 +1216,12 @@ const Type *MaxINode::add_ring( const Type *t0, const Type *t1 ) const {
|
|||||||
return TypeInt::make( MAX2(r0->_lo,r1->_lo), MAX2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) );
|
return TypeInt::make( MAX2(r0->_lo,r1->_lo), MAX2(r0->_hi,r1->_hi), MAX2(r0->_widen,r1->_widen) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if addition of an integer with type 't' and a constant 'c' can overflow
|
|
||||||
static bool can_overflow(const TypeInt* t, jint c) {
|
|
||||||
jint t_lo = t->_lo;
|
|
||||||
jint t_hi = t->_hi;
|
|
||||||
return ((c < 0 && (java_add(t_lo, c) > t_lo)) ||
|
|
||||||
(c > 0 && (java_add(t_hi, c) < t_hi)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ideal transformations for MaxINode
|
|
||||||
Node* MaxINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
|
||||||
// Force a right-spline graph
|
|
||||||
Node* l = in(1);
|
|
||||||
Node* r = in(2);
|
|
||||||
// Transform MaxI1(MaxI2(a, b), c) into MaxI1(a, MaxI2(b, c))
|
|
||||||
// to force a right-spline graph for the rest of MaxINode::Ideal().
|
|
||||||
if (l->Opcode() == Op_MaxI) {
|
|
||||||
assert(l != l->in(1), "dead loop in MaxINode::Ideal");
|
|
||||||
r = phase->transform(new MaxINode(l->in(2), r));
|
|
||||||
l = l->in(1);
|
|
||||||
set_req_X(1, l, phase);
|
|
||||||
set_req_X(2, r, phase);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get left input & constant
|
|
||||||
Node* x = l;
|
|
||||||
jint x_off = 0;
|
|
||||||
if (x->Opcode() == Op_AddI && // Check for "x+c0" and collect constant
|
|
||||||
x->in(2)->is_Con()) {
|
|
||||||
const Type* t = x->in(2)->bottom_type();
|
|
||||||
if (t == Type::TOP) return nullptr; // No progress
|
|
||||||
x_off = t->is_int()->get_con();
|
|
||||||
x = x->in(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan a right-spline-tree for MAXs
|
|
||||||
Node* y = r;
|
|
||||||
jint y_off = 0;
|
|
||||||
// Check final part of MAX tree
|
|
||||||
if (y->Opcode() == Op_AddI && // Check for "y+c1" and collect constant
|
|
||||||
y->in(2)->is_Con()) {
|
|
||||||
const Type* t = y->in(2)->bottom_type();
|
|
||||||
if (t == Type::TOP) return nullptr; // No progress
|
|
||||||
y_off = t->is_int()->get_con();
|
|
||||||
y = y->in(1);
|
|
||||||
}
|
|
||||||
if (x->_idx > y->_idx && r->Opcode() != Op_MaxI) {
|
|
||||||
swap_edges(1, 2);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeInt* tx = phase->type(x)->isa_int();
|
|
||||||
|
|
||||||
if (r->Opcode() == Op_MaxI) {
|
|
||||||
assert(r != r->in(2), "dead loop in MaxINode::Ideal");
|
|
||||||
y = r->in(1);
|
|
||||||
// Check final part of MAX tree
|
|
||||||
if (y->Opcode() == Op_AddI &&// Check for "y+c1" and collect constant
|
|
||||||
y->in(2)->is_Con()) {
|
|
||||||
const Type* t = y->in(2)->bottom_type();
|
|
||||||
if (t == Type::TOP) return nullptr; // No progress
|
|
||||||
y_off = t->is_int()->get_con();
|
|
||||||
y = y->in(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->_idx > y->_idx)
|
|
||||||
return new MaxINode(r->in(1), phase->transform(new MaxINode(l, r->in(2))));
|
|
||||||
|
|
||||||
// Transform MAX2(x + c0, MAX2(x + c1, z)) into MAX2(x + MAX2(c0, c1), z)
|
|
||||||
// if x == y and the additions can't overflow.
|
|
||||||
if (x == y && tx != nullptr &&
|
|
||||||
!can_overflow(tx, x_off) &&
|
|
||||||
!can_overflow(tx, y_off)) {
|
|
||||||
return new MaxINode(phase->transform(new AddINode(x, phase->intcon(MAX2(x_off, y_off)))), r->in(2));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Transform MAX2(x + c0, y + c1) into x + MAX2(c0, c1)
|
|
||||||
// if x == y and the additions can't overflow.
|
|
||||||
if (x == y && tx != nullptr &&
|
|
||||||
!can_overflow(tx, x_off) &&
|
|
||||||
!can_overflow(tx, y_off)) {
|
|
||||||
return new AddINode(x, phase->intcon(MAX2(x_off, y_off)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
//------------------------------Idealize---------------------------------------
|
//------------------------------Idealize---------------------------------------
|
||||||
// MINs show up in range-check loop limit calculations. Look for
|
// MINs show up in range-check loop limit calculations. Look for
|
||||||
// "MIN2(x+c0,MIN2(y,x+c1))". Pick the smaller constant: "MIN2(x+c0,y)"
|
// "MIN2(x+c0,MIN2(y,x+c1))". Pick the smaller constant: "MIN2(x+c0,y)"
|
||||||
Node *MinINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
Node* MinINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||||
Node *progress = nullptr;
|
return IdealI(phase, can_reshape);
|
||||||
// Force a right-spline graph
|
|
||||||
Node *l = in(1);
|
|
||||||
Node *r = in(2);
|
|
||||||
// Transform MinI1( MinI2(a,b), c) into MinI1( a, MinI2(b,c) )
|
|
||||||
// to force a right-spline graph for the rest of MinINode::Ideal().
|
|
||||||
if( l->Opcode() == Op_MinI ) {
|
|
||||||
assert( l != l->in(1), "dead loop in MinINode::Ideal" );
|
|
||||||
r = phase->transform(new MinINode(l->in(2),r));
|
|
||||||
l = l->in(1);
|
|
||||||
set_req_X(1, l, phase);
|
|
||||||
set_req_X(2, r, phase);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get left input & constant
|
|
||||||
Node *x = l;
|
|
||||||
jint x_off = 0;
|
|
||||||
if( x->Opcode() == Op_AddI && // Check for "x+c0" and collect constant
|
|
||||||
x->in(2)->is_Con() ) {
|
|
||||||
const Type *t = x->in(2)->bottom_type();
|
|
||||||
if( t == Type::TOP ) return nullptr; // No progress
|
|
||||||
x_off = t->is_int()->get_con();
|
|
||||||
x = x->in(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan a right-spline-tree for MINs
|
|
||||||
Node *y = r;
|
|
||||||
jint y_off = 0;
|
|
||||||
// Check final part of MIN tree
|
|
||||||
if( y->Opcode() == Op_AddI && // Check for "y+c1" and collect constant
|
|
||||||
y->in(2)->is_Con() ) {
|
|
||||||
const Type *t = y->in(2)->bottom_type();
|
|
||||||
if( t == Type::TOP ) return nullptr; // No progress
|
|
||||||
y_off = t->is_int()->get_con();
|
|
||||||
y = y->in(1);
|
|
||||||
}
|
|
||||||
if( x->_idx > y->_idx && r->Opcode() != Op_MinI ) {
|
|
||||||
swap_edges(1, 2);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeInt* tx = phase->type(x)->isa_int();
|
|
||||||
|
|
||||||
if( r->Opcode() == Op_MinI ) {
|
|
||||||
assert( r != r->in(2), "dead loop in MinINode::Ideal" );
|
|
||||||
y = r->in(1);
|
|
||||||
// Check final part of MIN tree
|
|
||||||
if( y->Opcode() == Op_AddI &&// Check for "y+c1" and collect constant
|
|
||||||
y->in(2)->is_Con() ) {
|
|
||||||
const Type *t = y->in(2)->bottom_type();
|
|
||||||
if( t == Type::TOP ) return nullptr; // No progress
|
|
||||||
y_off = t->is_int()->get_con();
|
|
||||||
y = y->in(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( x->_idx > y->_idx )
|
|
||||||
return new MinINode(r->in(1),phase->transform(new MinINode(l,r->in(2))));
|
|
||||||
|
|
||||||
// Transform MIN2(x + c0, MIN2(x + c1, z)) into MIN2(x + MIN2(c0, c1), z)
|
|
||||||
// if x == y and the additions can't overflow.
|
|
||||||
if (x == y && tx != nullptr &&
|
|
||||||
!can_overflow(tx, x_off) &&
|
|
||||||
!can_overflow(tx, y_off)) {
|
|
||||||
return new MinINode(phase->transform(new AddINode(x, phase->intcon(MIN2(x_off, y_off)))), r->in(2));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Transform MIN2(x + c0, y + c1) into x + MIN2(c0, c1)
|
|
||||||
// if x == y and the additions can't overflow.
|
|
||||||
if (x == y && tx != nullptr &&
|
|
||||||
!can_overflow(tx, x_off) &&
|
|
||||||
!can_overflow(tx, y_off)) {
|
|
||||||
return new AddINode(x,phase->intcon(MIN2(x_off,y_off)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------add_ring---------------------------------------
|
//------------------------------add_ring---------------------------------------
|
||||||
|
@ -28,10 +28,12 @@
|
|||||||
#include "opto/node.hpp"
|
#include "opto/node.hpp"
|
||||||
#include "opto/opcodes.hpp"
|
#include "opto/opcodes.hpp"
|
||||||
#include "opto/type.hpp"
|
#include "opto/type.hpp"
|
||||||
|
#include "utilities/pair.hpp"
|
||||||
|
|
||||||
// Portions of code courtesy of Clifford Click
|
// Portions of code courtesy of Clifford Click
|
||||||
|
|
||||||
class PhaseTransform;
|
class PhaseTransform;
|
||||||
|
typedef const Pair<Node*, jint> ConstAddOperands;
|
||||||
|
|
||||||
//------------------------------AddNode----------------------------------------
|
//------------------------------AddNode----------------------------------------
|
||||||
// Classic Add functionality. This covers all the usual 'add' behaviors for
|
// Classic Add functionality. This covers all the usual 'add' behaviors for
|
||||||
@ -252,12 +254,14 @@ class MaxNode : public AddNode {
|
|||||||
private:
|
private:
|
||||||
static Node* build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn);
|
static Node* build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn);
|
||||||
static Node* build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn);
|
static Node* build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn);
|
||||||
|
Node* extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MaxNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {}
|
MaxNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {}
|
||||||
virtual int Opcode() const = 0;
|
virtual int Opcode() const = 0;
|
||||||
virtual int max_opcode() const = 0;
|
virtual int max_opcode() const = 0;
|
||||||
virtual int min_opcode() const = 0;
|
virtual int min_opcode() const = 0;
|
||||||
|
Node* IdealI(PhaseGVN* phase, bool can_reshape);
|
||||||
|
|
||||||
static Node* unsigned_max(Node* a, Node* b, const Type* t, PhaseGVN& gvn) {
|
static Node* unsigned_max(Node* a, Node* b, const Type* t, PhaseGVN& gvn) {
|
||||||
return build_min_max(a, b, true, true, t, gvn);
|
return build_min_max(a, b, true, true, t, gvn);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2022, Arm Limited. All rights reserved.
|
* Copyright (c) 2022, Arm Limited. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
@ -39,22 +40,38 @@ public class MaxMinINodeIdealizationTests {
|
|||||||
TestFramework.run();
|
TestFramework.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Run(test = {"testMax1", "testMax2", "testMax3", "testMin1", "testMin2", "testMin3"})
|
@Run(test = {"testMax1LL", "testMax1LR", "testMax1RL", "testMax1RR",
|
||||||
public void runMethod() {
|
"testMax1LLNoInnerAdd", "testMax1LLNoInnerAdd2", "testMax1LLNoOuterAdd", "testMax1LLNoAdd",
|
||||||
|
"testMax2L", "testMax2R",
|
||||||
|
"testMax2LNoLeftAdd",
|
||||||
|
"testMax3",
|
||||||
|
"testMin1",
|
||||||
|
"testMin2",
|
||||||
|
"testMin3"})
|
||||||
|
public void runPositiveTests() {
|
||||||
int a = RunInfo.getRandom().nextInt();
|
int a = RunInfo.getRandom().nextInt();
|
||||||
int min = Integer.MIN_VALUE;
|
int min = Integer.MIN_VALUE;
|
||||||
int max = Integer.MAX_VALUE;
|
int max = Integer.MAX_VALUE;
|
||||||
|
|
||||||
assertResult(a);
|
assertPositiveResult(a);
|
||||||
assertResult(0);
|
assertPositiveResult(0);
|
||||||
assertResult(min);
|
assertPositiveResult(min);
|
||||||
assertResult(max);
|
assertPositiveResult(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DontCompile
|
@DontCompile
|
||||||
public void assertResult(int a) {
|
public void assertPositiveResult(int a) {
|
||||||
Asserts.assertEQ(Math.max(((a >> 1) + 100), Math.max(((a >> 1) + 150), 200)), testMax1(a));
|
Asserts.assertEQ(Math.max(Math.max(((a >> 1) + 150), 200), ((a >> 1) + 100)), testMax1LL(a));
|
||||||
Asserts.assertEQ(Math.max(((a >> 1) + 10), ((a >> 1) + 11)) , testMax2(a));
|
Asserts.assertEQ(testMax1LL(a) , testMax1LR(a));
|
||||||
|
Asserts.assertEQ(testMax1LL(a) , testMax1RL(a));
|
||||||
|
Asserts.assertEQ(testMax1LL(a) , testMax1RR(a));
|
||||||
|
Asserts.assertEQ(Math.max(Math.max((a >> 1), 200), (a >> 1) + 100) , testMax1LLNoInnerAdd(a));
|
||||||
|
Asserts.assertEQ(Math.max(Math.max((a >> 1), (a << 1)), (a >> 1) + 100) , testMax1LLNoInnerAdd2(a));
|
||||||
|
Asserts.assertEQ(Math.max(Math.max(((a >> 1) + 150), 200), a >> 1) , testMax1LLNoOuterAdd(a));
|
||||||
|
Asserts.assertEQ(Math.max(Math.max((a >> 1), 200), a >> 1) , testMax1LLNoAdd(a));
|
||||||
|
Asserts.assertEQ(Math.max(((a >> 1) + 10), ((a >> 1) + 11)) , testMax2L(a));
|
||||||
|
Asserts.assertEQ(testMax2L(a) , testMax2R(a));
|
||||||
|
Asserts.assertEQ(Math.max(a >> 1, ((a >> 1) + 11)) , testMax2LNoLeftAdd(a));
|
||||||
Asserts.assertEQ(Math.max(a, a) , testMax3(a));
|
Asserts.assertEQ(Math.max(a, a) , testMax3(a));
|
||||||
|
|
||||||
Asserts.assertEQ(Math.min(((a >> 1) + 100), Math.min(((a >> 1) + 150), 200)), testMin1(a));
|
Asserts.assertEQ(Math.min(((a >> 1) + 100), Math.min(((a >> 1) + 150), 200)), testMin1(a));
|
||||||
@ -72,10 +89,65 @@ public class MaxMinINodeIdealizationTests {
|
|||||||
@IR(counts = {IRNode.MAX_I, "1",
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
IRNode.ADD , "1",
|
IRNode.ADD , "1",
|
||||||
})
|
})
|
||||||
public int testMax1(int i) {
|
public int testMax1LL(int i) {
|
||||||
|
return Math.max(Math.max(((i >> 1) + 150), 200), ((i >> 1) + 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
|
IRNode.ADD , "1",
|
||||||
|
})
|
||||||
|
public int testMax1LR(int i) {
|
||||||
|
return Math.max(Math.max(200, ((i >> 1) + 150)), ((i >> 1) + 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
|
IRNode.ADD , "1",
|
||||||
|
})
|
||||||
|
public int testMax1RL(int i) {
|
||||||
return Math.max(((i >> 1) + 100), Math.max(((i >> 1) + 150), 200));
|
return Math.max(((i >> 1) + 100), Math.max(((i >> 1) + 150), 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
|
IRNode.ADD , "1",
|
||||||
|
})
|
||||||
|
public int testMax1RR(int i) {
|
||||||
|
return Math.max(((i >> 1) + 100), Math.max(200, ((i >> 1) + 150)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
|
IRNode.ADD , "1",
|
||||||
|
})
|
||||||
|
public int testMax1LLNoInnerAdd(int i) {
|
||||||
|
return Math.max(Math.max((i >> 1), 200), (i >> 1) + 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
|
IRNode.ADD , "1",
|
||||||
|
})
|
||||||
|
public int testMax1LLNoInnerAdd2(int i) {
|
||||||
|
return Math.max(Math.max((i >> 1), (i << 1)), (i >> 1) + 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1",
|
||||||
|
IRNode.ADD , "1",
|
||||||
|
})
|
||||||
|
public int testMax1LLNoOuterAdd(int i) {
|
||||||
|
return Math.max(Math.max(((i >> 1) + 150), 200), i >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(failOn = {IRNode.ADD})
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1"})
|
||||||
|
public int testMax1LLNoAdd(int i) {
|
||||||
|
return Math.max(Math.max((i >> 1), 200), i >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Similarly, transform min(x + c0, min(y + c1, z)) to min(add(x, c2), z) if x == y, where c2 = MIN2(c0, c1).
|
// Similarly, transform min(x + c0, min(y + c1, z)) to min(add(x, c2), z) if x == y, where c2 = MIN2(c0, c1).
|
||||||
@Test
|
@Test
|
||||||
@IR(counts = {IRNode.MIN_I, "1",
|
@IR(counts = {IRNode.MIN_I, "1",
|
||||||
@ -91,10 +163,24 @@ public class MaxMinINodeIdealizationTests {
|
|||||||
@Test
|
@Test
|
||||||
@IR(failOn = {IRNode.MAX_I})
|
@IR(failOn = {IRNode.MAX_I})
|
||||||
@IR(counts = {IRNode.ADD, "1"})
|
@IR(counts = {IRNode.ADD, "1"})
|
||||||
public int testMax2(int i) {
|
public int testMax2L(int i) {
|
||||||
return Math.max((i >> 1) + 10, (i >> 1) + 11);
|
return Math.max((i >> 1) + 10, (i >> 1) + 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(failOn = {IRNode.MAX_I})
|
||||||
|
@IR(counts = {IRNode.ADD, "1"})
|
||||||
|
public int testMax2R(int i) {
|
||||||
|
return Math.max((i >> 1) + 11, (i >> 1) + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(failOn = {IRNode.MAX_I})
|
||||||
|
@IR(counts = {IRNode.ADD, "1"})
|
||||||
|
public int testMax2LNoLeftAdd(int i) {
|
||||||
|
return Math.max(i >> 1, (i >> 1) + 11);
|
||||||
|
}
|
||||||
|
|
||||||
// Similarly, transform min(x + c0, y + c1) to add(x, c2) if x == y, where c2 = MIN2(c0, c1).
|
// Similarly, transform min(x + c0, y + c1) to add(x, c2) if x == y, where c2 = MIN2(c0, c1).
|
||||||
@Test
|
@Test
|
||||||
@IR(failOn = {IRNode.MIN_I})
|
@IR(failOn = {IRNode.MIN_I})
|
||||||
@ -116,4 +202,76 @@ public class MaxMinINodeIdealizationTests {
|
|||||||
public int testMin3(int i) {
|
public int testMin3(int i) {
|
||||||
return Math.min(i, i);
|
return Math.min(i, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Run(test = {"testTwoLevelsDifferentXY",
|
||||||
|
"testTwoLevelsNoLeftConstant",
|
||||||
|
"testTwoLevelsNoRightConstant",
|
||||||
|
"testDifferentXY",
|
||||||
|
"testNoLeftConstant",
|
||||||
|
"testNoRightConstant"})
|
||||||
|
public void runNegativeTests() {
|
||||||
|
int a = RunInfo.getRandom().nextInt();
|
||||||
|
int min = Integer.MIN_VALUE;
|
||||||
|
int max = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
assertNegativeResult(a);
|
||||||
|
assertNegativeResult(0);
|
||||||
|
assertNegativeResult(min);
|
||||||
|
assertNegativeResult(max);
|
||||||
|
|
||||||
|
testTwoLevelsDifferentXY(10);
|
||||||
|
testTwoLevelsNoLeftConstant(10, 42);
|
||||||
|
testTwoLevelsNoRightConstant(10, 42);
|
||||||
|
testDifferentXY(10);
|
||||||
|
testNoLeftConstant(10, 42);
|
||||||
|
testNoRightConstant(10, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DontCompile
|
||||||
|
public void assertNegativeResult(int a) {
|
||||||
|
Asserts.assertEQ(Math.max(Math.max(((a >> 1) + 150), 200), ((a >> 2) + 100)), testTwoLevelsDifferentXY(a));
|
||||||
|
Asserts.assertEQ(Math.max(Math.max(((a >> 1) + a*2), 200), ((a >> 1) + 100)), testTwoLevelsNoLeftConstant(a, a*2));
|
||||||
|
Asserts.assertEQ(Math.max(Math.max(((a >> 1) + 150), 200), ((a >> 1) + a*2)), testTwoLevelsNoRightConstant(a, a*2));
|
||||||
|
Asserts.assertEQ(Math.max((a >> 1) + 10, (a >> 2) + 11), testDifferentXY(a));
|
||||||
|
Asserts.assertEQ(Math.max((a >> 1) + a*2, (a >> 1) + 11), testNoLeftConstant(a, a*2));
|
||||||
|
Asserts.assertEQ(Math.max((a >> 1) + 10, (a >> 1) + a*2), testNoRightConstant(a, a*2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "2"})
|
||||||
|
public int testTwoLevelsDifferentXY(int i) {
|
||||||
|
return Math.max(Math.max(((i >> 1) + 150), 200), ((i >> 2) + 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "2"})
|
||||||
|
public int testTwoLevelsNoLeftConstant(int i, int c0) {
|
||||||
|
return Math.max(Math.max(((i >> 1) + c0), 200), ((i >> 1) + 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "2"})
|
||||||
|
public int testTwoLevelsNoRightConstant(int i, int c1) {
|
||||||
|
return Math.max(Math.max(((i >> 1) + 150), 200), ((i >> 1) + c1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1"})
|
||||||
|
public int testDifferentXY(int i) {
|
||||||
|
return Math.max((i >> 1) + 10, (i >> 2) + 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1"})
|
||||||
|
public int testNoLeftConstant(int i, int c0) {
|
||||||
|
return Math.max((i >> 1) + c0, (i >> 1) + 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(counts = {IRNode.MAX_I, "1"})
|
||||||
|
public int testNoRightConstant(int i, int c1) {
|
||||||
|
return Math.max((i >> 1) + 10, (i >> 1) + c1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -794,6 +794,16 @@ public class IRNode {
|
|||||||
superWordNodes(MUL_REDUCTION_VL, "MulReductionVL");
|
superWordNodes(MUL_REDUCTION_VL, "MulReductionVL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final String MIN_REDUCTION_V = PREFIX + "MIN_REDUCTION_V" + POSTFIX;
|
||||||
|
static {
|
||||||
|
superWordNodes(MIN_REDUCTION_V, "MinReductionV");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String MAX_REDUCTION_V = PREFIX + "MAX_REDUCTION_V" + POSTFIX;
|
||||||
|
static {
|
||||||
|
superWordNodes(MAX_REDUCTION_V, "MaxReductionV");
|
||||||
|
}
|
||||||
|
|
||||||
public static final String NEG_V = PREFIX + "NEG_V" + POSTFIX;
|
public static final String NEG_V = PREFIX + "NEG_V" + POSTFIX;
|
||||||
static {
|
static {
|
||||||
beforeMatchingNameRegex(NEG_V, "NegV(F|D)");
|
beforeMatchingNameRegex(NEG_V, "NegV(F|D)");
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8302673
|
||||||
|
* @summary [SuperWord] MaxReduction and MinReduction should vectorize for int
|
||||||
|
* @library /test/lib /
|
||||||
|
* @run driver compiler.loopopts.superword.MinMaxRed_Int
|
||||||
|
*/
|
||||||
|
|
||||||
|
package compiler.loopopts.superword;
|
||||||
|
|
||||||
|
import compiler.lib.ir_framework.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
import jdk.test.lib.Utils;
|
||||||
|
|
||||||
|
public class MinMaxRed_Int {
|
||||||
|
|
||||||
|
private static final Random random = Utils.getRandomInstance();
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
TestFramework framework = new TestFramework();
|
||||||
|
framework.addFlags("-XX:+IgnoreUnrecognizedVMOptions",
|
||||||
|
"-XX:LoopUnrollLimit=250",
|
||||||
|
"-XX:CompileThresholdScaling=0.1");
|
||||||
|
framework.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Run(test = {"maxReductionImplement"},
|
||||||
|
mode = RunMode.STANDALONE)
|
||||||
|
public void runMaxTest() {
|
||||||
|
int[] a = new int[1024];
|
||||||
|
int[] b = new int[1024];
|
||||||
|
ReductionInit(a, b);
|
||||||
|
int res = 0;
|
||||||
|
for (int j = 0; j < 2000; j++) {
|
||||||
|
res = maxReductionImplement(a, b, res);
|
||||||
|
}
|
||||||
|
if (res == Arrays.stream(a).max().getAsInt()) {
|
||||||
|
System.out.println("Success");
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Run(test = {"minReductionImplement"},
|
||||||
|
mode = RunMode.STANDALONE)
|
||||||
|
public void runMinTest() {
|
||||||
|
int[] a = new int[1024];
|
||||||
|
int[] b = new int[1024];
|
||||||
|
ReductionInit(a, b);
|
||||||
|
int res = 1;
|
||||||
|
for (int j = 0; j < 2000; j++) {
|
||||||
|
res = minReductionImplement(a, b, res);
|
||||||
|
}
|
||||||
|
if (res == Arrays.stream(a).min().getAsInt()) {
|
||||||
|
System.out.println("Success");
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReductionInit(int[] a, int[] b) {
|
||||||
|
for (int i = 0; i < a.length; i++) {
|
||||||
|
a[i] = random.nextInt();
|
||||||
|
b[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(applyIf = {"SuperWordReductions", "true"},
|
||||||
|
applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"},
|
||||||
|
counts = {IRNode.MIN_REDUCTION_V, " > 0"})
|
||||||
|
public static int minReductionImplement(int[] a, int[] b, int res) {
|
||||||
|
for (int i = 0; i < a.length; i++) {
|
||||||
|
res = Math.min(res, a[i] * b[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IR(applyIf = {"SuperWordReductions", "true"},
|
||||||
|
applyIfCPUFeatureOr = { "sse4.1", "true" , "asimd" , "true"},
|
||||||
|
counts = {IRNode.MAX_REDUCTION_V, " > 0"})
|
||||||
|
public static int maxReductionImplement(int[] a, int[] b, int res) {
|
||||||
|
for (int i = 0; i < a.length; i++) {
|
||||||
|
res = Math.max(res, a[i] * b[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user