8248830: C2: Optimize Rotate API on x86

Improved existing scalar rotate operations, added support for vector rotate operations using new AVX512 instructions.

Reviewed-by: vlivanov, kvn
This commit is contained in:
Jatin Bhateja 2020-08-09 02:03:09 +05:30
parent 084e15bca3
commit 3ed56830b4
19 changed files with 1474 additions and 282 deletions

View file

@ -24,8 +24,11 @@
#include "precompiled.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/connode.hpp"
#include "opto/mulnode.hpp"
#include "opto/subnode.hpp"
#include "opto/vectornode.hpp"
#include "utilities/powerOfTwo.hpp"
#include "utilities/globalDefinitions.hpp"
//------------------------------VectorNode--------------------------------------
@ -132,6 +135,12 @@ int VectorNode::opcode(int sopc, BasicType bt) {
case Op_RoundDoubleMode:
assert(bt == T_DOUBLE, "must be");
return Op_RoundDoubleModeV;
case Op_RotateLeft:
assert(bt == T_LONG || bt == T_INT, "must be");
return Op_RotateLeftV;
case Op_RotateRight:
assert(bt == T_LONG || bt == T_INT, "must be");
return Op_RotateRightV;
case Op_SqrtF:
assert(bt == T_FLOAT, "must be");
return Op_SqrtVF;
@ -239,6 +248,12 @@ bool VectorNode::implemented(int opc, uint vlen, BasicType bt) {
(vlen > 1) && is_power_of_2(vlen) &&
Matcher::vector_size_supported(bt, vlen)) {
int vopc = VectorNode::opcode(opc, bt);
// For rotate operation we will do a lazy de-generation into
// OrV/LShiftV/URShiftV pattern if the target does not support
// vector rotation instruction.
if (vopc == Op_RotateLeftV || vopc == Op_RotateRightV) {
return is_vector_rotate_supported(vopc, vlen, bt);
}
return vopc > 0 && Matcher::match_rule_supported_vector(vopc, vlen, bt);
}
return false;
@ -263,13 +278,45 @@ bool VectorNode::is_muladds2i(Node* n) {
return false;
}
bool VectorNode::is_roundopD(Node *n) {
bool VectorNode::is_roundopD(Node* n) {
if (n->Opcode() == Op_RoundDoubleMode) {
return true;
}
return false;
}
bool VectorNode::is_scalar_rotate(Node* n) {
if (n->Opcode() == Op_RotateLeft || n->Opcode() == Op_RotateRight) {
return true;
}
return false;
}
bool VectorNode::is_vector_rotate_supported(int vopc, uint vlen, BasicType bt) {
assert(vopc == Op_RotateLeftV || vopc == Op_RotateRightV, "wrong opcode");
// If target defines vector rotation patterns then no
// need for degeneration.
if (Matcher::match_rule_supported_vector(vopc, vlen, bt)) {
return true;
}
// Validate existence of nodes created in case of rotate degeneration.
switch (bt) {
case T_INT:
return Matcher::match_rule_supported_vector(Op_OrV, vlen, bt) &&
Matcher::match_rule_supported_vector(Op_LShiftVI, vlen, bt) &&
Matcher::match_rule_supported_vector(Op_URShiftVI, vlen, bt);
case T_LONG:
return Matcher::match_rule_supported_vector(Op_OrV, vlen, bt) &&
Matcher::match_rule_supported_vector(Op_LShiftVL, vlen, bt) &&
Matcher::match_rule_supported_vector(Op_URShiftVL, vlen, bt);
default:
assert(false, "not supported: %s", type2name(bt));
return false;
}
}
bool VectorNode::is_shift(Node* n) {
switch (n->Opcode()) {
case Op_LShiftI:
@ -395,6 +442,8 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b
case Op_SqrtVD: return new SqrtVDNode(n1, vt);
case Op_PopCountVI: return new PopCountVINode(n1, vt);
case Op_RotateLeftV: return new RotateLeftVNode(n1, n2, vt);
case Op_RotateRightV: return new RotateRightVNode(n1, n2, vt);
case Op_LShiftVB: return new LShiftVBNode(n1, n2, vt);
case Op_LShiftVS: return new LShiftVSNode(n1, n2, vt);
@ -784,3 +833,161 @@ MacroLogicVNode* MacroLogicVNode::make(PhaseGVN& gvn, Node* in1, Node* in2, Node
return new MacroLogicVNode(in1, in2, in3, fn, vt);
}
Node* VectorNode::degenerate_vector_rotate(Node* src, Node* cnt, bool is_rotate_left,
int vlen, BasicType bt, PhaseGVN* phase) {
int shiftLOpc;
int shiftROpc;
Node* shiftLCnt = NULL;
Node* shiftRCnt = NULL;
const TypeVect* vt = TypeVect::make(bt, vlen);
// Compute shift values for right rotation and
// later swap them in case of left rotation.
if (cnt->is_Con()) {
// Constant shift case.
if (bt == T_INT) {
int shift = cnt->get_int() & 31;
shiftRCnt = phase->intcon(shift);
shiftLCnt = phase->intcon(32 - shift);
shiftLOpc = Op_LShiftI;
shiftROpc = Op_URShiftI;
} else {
int shift = cnt->get_int() & 63;
shiftRCnt = phase->intcon(shift);
shiftLCnt = phase->intcon(64 - shift);
shiftLOpc = Op_LShiftL;
shiftROpc = Op_URShiftL;
}
} else {
// Variable shift case.
assert(VectorNode::is_invariant_vector(cnt), "Broadcast expected");
cnt = cnt->in(1);
if (bt == T_INT) {
shiftRCnt = phase->transform(new AndINode(cnt, phase->intcon(31)));
shiftLCnt = phase->transform(new SubINode(phase->intcon(32), shiftRCnt));
shiftLOpc = Op_LShiftI;
shiftROpc = Op_URShiftI;
} else {
assert(cnt->Opcode() == Op_ConvI2L, "ConvI2L expected");
cnt = cnt->in(1);
shiftRCnt = phase->transform(new AndINode(cnt, phase->intcon(63)));
shiftLCnt = phase->transform(new SubINode(phase->intcon(64), shiftRCnt));
shiftLOpc = Op_LShiftL;
shiftROpc = Op_URShiftL;
}
}
// Swap the computed left and right shift counts.
if (is_rotate_left) {
swap(shiftRCnt,shiftLCnt);
}
shiftLCnt = phase->transform(new LShiftCntVNode(shiftLCnt, vt));
shiftRCnt = phase->transform(new RShiftCntVNode(shiftRCnt, vt));
return new OrVNode(phase->transform(VectorNode::make(shiftLOpc, src, shiftLCnt, vlen, bt)),
phase->transform(VectorNode::make(shiftROpc, src, shiftRCnt, vlen, bt)),
vt);
}
Node* RotateLeftVNode::Ideal(PhaseGVN* phase, bool can_reshape) {
int vlen = length();
BasicType bt = vect_type()->element_basic_type();
if (!Matcher::match_rule_supported_vector(Op_RotateLeftV, vlen, bt)) {
return VectorNode::degenerate_vector_rotate(in(1), in(2), true, vlen, bt, phase);
}
return NULL;
}
Node* RotateRightVNode::Ideal(PhaseGVN* phase, bool can_reshape) {
int vlen = length();
BasicType bt = vect_type()->element_basic_type();
if (!Matcher::match_rule_supported_vector(Op_RotateRightV, vlen, bt)) {
return VectorNode::degenerate_vector_rotate(in(1), in(2), false, vlen, bt, phase);
}
return NULL;
}
Node* OrVNode::Ideal(PhaseGVN* phase, bool can_reshape) {
int lopcode = in(1)->Opcode();
int ropcode = in(2)->Opcode();
const TypeVect* vt = bottom_type()->is_vect();
int vec_len = vt->length();
BasicType bt = vt->element_basic_type();
// Vector Rotate operations inferencing, this will be useful when vector
// operations are created via non-SLP route i.e. (VectorAPI).
if (Matcher::match_rule_supported_vector(Op_RotateLeftV, vec_len, bt) &&
((ropcode == Op_LShiftVI && lopcode == Op_URShiftVI) ||
(ropcode == Op_LShiftVL && lopcode == Op_URShiftVL)) &&
in(1)->in(1) == in(2)->in(1)) {
assert(Op_RShiftCntV == in(1)->in(2)->Opcode(), "LShiftCntV operand expected");
assert(Op_LShiftCntV == in(2)->in(2)->Opcode(), "RShiftCntV operand expected");
Node* lshift = in(1)->in(2)->in(1);
Node* rshift = in(2)->in(2)->in(1);
int mod_val = bt == T_LONG ? 64 : 32;
int shift_mask = bt == T_LONG ? 0x3F : 0x1F;
// val >> norm_con_shift | val << (32 - norm_con_shift) => rotate_right val ,
// norm_con_shift
if (lshift->is_Con() && rshift->is_Con() &&
((lshift->get_int() & shift_mask) ==
(mod_val - (rshift->get_int() & shift_mask)))) {
return new RotateRightVNode(
in(1)->in(1), phase->intcon(lshift->get_int() & shift_mask), vt);
}
if (lshift->Opcode() == Op_AndI && rshift->Opcode() == Op_AndI &&
lshift->in(2)->is_Con() && rshift->in(2)->is_Con() &&
lshift->in(2)->get_int() == (mod_val - 1) &&
rshift->in(2)->get_int() == (mod_val - 1)) {
lshift = lshift->in(1);
rshift = rshift->in(1);
// val << var_shift | val >> (0/32 - var_shift) => rotate_left val ,
// var_shift
if (lshift->Opcode() == Op_SubI && lshift->in(2) == rshift &&
lshift->in(1)->is_Con() &&
(lshift->in(1)->get_int() == 0 ||
lshift->in(1)->get_int() == mod_val)) {
Node* rotate_cnt = phase->transform(new ReplicateINode(rshift, vt));
return new RotateLeftVNode(in(1)->in(1), rotate_cnt, vt);
}
}
}
if (Matcher::match_rule_supported_vector(Op_RotateRightV, vec_len, bt) &&
((ropcode == Op_URShiftVI && lopcode == Op_LShiftVI) ||
(ropcode == Op_URShiftVL && lopcode == Op_LShiftVL)) &&
in(1)->in(1) == in(2)->in(1)) {
assert(Op_LShiftCntV == in(1)->in(2)->Opcode(), "RShiftCntV operand expected");
assert(Op_RShiftCntV == in(2)->in(2)->Opcode(), "LShiftCntV operand expected");
Node* rshift = in(1)->in(2)->in(1);
Node* lshift = in(2)->in(2)->in(1);
int mod_val = bt == T_LONG ? 64 : 32;
int shift_mask = bt == T_LONG ? 0x3F : 0x1F;
// val << norm_con_shift | val >> (32 - norm_con_shift) => rotate_left val
// , norm_con_shift
if (rshift->is_Con() && lshift->is_Con() &&
((rshift->get_int() & shift_mask) ==
(mod_val - (lshift->get_int() & shift_mask)))) {
return new RotateLeftVNode(
in(1)->in(1), phase->intcon(rshift->get_int() & shift_mask), vt);
}
if (lshift->Opcode() == Op_AndI && rshift->Opcode() == Op_AndI &&
lshift->in(2)->is_Con() && rshift->in(2)->is_Con() &&
rshift->in(2)->get_int() == (mod_val - 1) &&
lshift->in(2)->get_int() == (mod_val - 1)) {
rshift = rshift->in(1);
lshift = lshift->in(1);
// val >> var_shift | val << (0/32 - var_shift) => rotate_right val ,
// var_shift
if (rshift->Opcode() == Op_SubI && rshift->in(2) == lshift &&
rshift->in(1)->is_Con() &&
(rshift->in(1)->get_int() == 0 ||
rshift->in(1)->get_int() == mod_val)) {
Node* rotate_cnt = phase->transform(new ReplicateINode(lshift, vt));
return new RotateRightVNode(in(1)->in(1), rotate_cnt, vt);
}
}
}
return NULL;
}