/* * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
// Return the vector operator for the specified scalar operation // and vector length. int VectorNode::opcode(int sopc, BasicType bt) { switch (sopc) { case Op_AddI: switch (bt) { case T_BOOLEAN: case T_BYTE: return Op_AddVB; case T_CHAR: case T_SHORT: return Op_AddVS; case T_INT: return Op_AddVI; default: return 0;
} case Op_AddL: return (bt == T_LONG ? Op_AddVL : 0); case Op_AddF: return (bt == T_FLOAT ? Op_AddVF : 0); case Op_AddD: return (bt == T_DOUBLE ? Op_AddVD : 0);
case Op_SubI: switch (bt) { case T_BOOLEAN: case T_BYTE: return Op_SubVB; case T_CHAR: case T_SHORT: return Op_SubVS; case T_INT: return Op_SubVI; default: return 0;
} case Op_SubL: return (bt == T_LONG ? Op_SubVL : 0); case Op_SubF: return (bt == T_FLOAT ? Op_SubVF : 0); case Op_SubD: return (bt == T_DOUBLE ? Op_SubVD : 0);
case Op_MulI: switch (bt) { case T_BOOLEAN:return 0; case T_BYTE: return Op_MulVB; case T_CHAR: case T_SHORT: return Op_MulVS; case T_INT: return Op_MulVI; default: return 0;
} case Op_MulL: return (bt == T_LONG ? Op_MulVL : 0); case Op_MulF: return (bt == T_FLOAT ? Op_MulVF : 0); case Op_MulD: return (bt == T_DOUBLE ? Op_MulVD : 0); case Op_FmaD: return (bt == T_DOUBLE ? Op_FmaVD : 0); case Op_FmaF: return (bt == T_FLOAT ? Op_FmaVF : 0); case Op_CMoveF: return (bt == T_FLOAT ? Op_CMoveVF : 0); case Op_CMoveD: return (bt == T_DOUBLE ? Op_CMoveVD : 0); case Op_DivF: return (bt == T_FLOAT ? Op_DivVF : 0); case Op_DivD: return (bt == T_DOUBLE ? Op_DivVD : 0); case Op_AbsI: switch (bt) { case T_BOOLEAN: case T_CHAR: return 0; // abs does not make sense for unsigned case T_BYTE: return Op_AbsVB; case T_SHORT: return Op_AbsVS; case T_INT: return Op_AbsVI; default: return 0;
} case Op_AbsL: return (bt == T_LONG ? Op_AbsVL : 0); case Op_MinI: switch (bt) { case T_BOOLEAN: case T_CHAR: return 0; case T_BYTE: case T_SHORT: case T_INT: return Op_MinV; default: return 0;
} case Op_MinL: return (bt == T_LONG ? Op_MinV : 0); case Op_MinF: return (bt == T_FLOAT ? Op_MinV : 0); case Op_MinD: return (bt == T_DOUBLE ? Op_MinV : 0); case Op_MaxI: switch (bt) { case T_BOOLEAN: case T_CHAR: return 0; case T_BYTE: case T_SHORT: case T_INT: return Op_MaxV; default: return 0;
} case Op_MaxL: return (bt == T_LONG ? Op_MaxV : 0); case Op_MaxF: return (bt == T_FLOAT ? Op_MaxV : 0); case Op_MaxD: return (bt == T_DOUBLE ? Op_MaxV : 0); case Op_AbsF: return (bt == T_FLOAT ? Op_AbsVF : 0); case Op_AbsD: return (bt == T_DOUBLE ? Op_AbsVD : 0); case Op_NegI: switch (bt) { case T_BYTE: case T_SHORT: case T_INT: return Op_NegVI; default: return 0;
} case Op_NegL: return (bt == T_LONG ? Op_NegVL : 0); case Op_NegF: return (bt == T_FLOAT ? Op_NegVF : 0); case Op_NegD: return (bt == T_DOUBLE ? Op_NegVD : 0); case Op_RoundDoubleMode: return (bt == T_DOUBLE ? Op_RoundDoubleModeV : 0); case Op_RotateLeft: return (is_integral_type(bt) ? Op_RotateLeftV : 0); case Op_RotateRight: return (is_integral_type(bt) ? Op_RotateRightV : 0); case Op_SqrtF: return (bt == T_FLOAT ? Op_SqrtVF : 0); case Op_SqrtD: return (bt == T_DOUBLE ? Op_SqrtVD : 0); case Op_RoundF: return (bt == T_INT ? Op_RoundVF : 0); case Op_RoundD: return (bt == T_LONG ? Op_RoundVD : 0); case Op_PopCountI: return Op_PopCountVI; case Op_PopCountL: return Op_PopCountVL; case Op_ReverseI: case Op_ReverseL: return (is_integral_type(bt) ? Op_ReverseV : 0); case Op_ReverseBytesS: case Op_ReverseBytesUS: // Subword operations in superword usually don't have precise info // about signedness. But the behavior of reverseBytes for short and // char are exactly the same. return ((bt == T_SHORT || bt == T_CHAR) ? Op_ReverseBytesV : 0); case Op_ReverseBytesI: // There is no reverseBytes() in Byte class but T_BYTE may appear // in VectorAPI calls. We still use ReverseBytesI for T_BYTE to // ensure vector intrinsification succeeds. return ((bt == T_INT || bt == T_BYTE) ? Op_ReverseBytesV : 0); case Op_ReverseBytesL: return (bt == T_LONG ? Op_ReverseBytesV : 0); case Op_CompressBits: // Not implemented. Returning 0 temporarily return 0; case Op_ExpandBits: // Not implemented. Returning 0 temporarily return 0; case Op_LShiftI: switch (bt) { case T_BOOLEAN: case T_BYTE: return Op_LShiftVB; case T_CHAR: case T_SHORT: return Op_LShiftVS; case T_INT: return Op_LShiftVI; default: return 0;
} case Op_LShiftL: return (bt == T_LONG ? Op_LShiftVL : 0); case Op_RShiftI: switch (bt) { case T_BOOLEAN:return Op_URShiftVB; // boolean is unsigned value case T_CHAR: return Op_URShiftVS; // char is unsigned value case T_BYTE: return Op_RShiftVB; case T_SHORT: return Op_RShiftVS; case T_INT: return Op_RShiftVI; default: return 0;
} case Op_RShiftL: return (bt == T_LONG ? Op_RShiftVL : 0); case Op_URShiftB: return (bt == T_BYTE ? Op_URShiftVB : 0); case Op_URShiftS: return (bt == T_SHORT ? Op_URShiftVS : 0); case Op_URShiftI: switch (bt) { case T_BOOLEAN:return Op_URShiftVB; case T_CHAR: return Op_URShiftVS; case T_BYTE: case T_SHORT: return 0; // Vector logical right shift for signed short // values produces incorrect Java result for // negative data because java code should convert // a short value into int value with sign // extension before a shift. case T_INT: return Op_URShiftVI; default: return 0;
} case Op_URShiftL: return (bt == T_LONG ? Op_URShiftVL : 0); case Op_AndI: case Op_AndL: return Op_AndV; case Op_OrI: case Op_OrL: return Op_OrV; case Op_XorI: case Op_XorL: return Op_XorV;
case Op_LoadB: case Op_LoadUB: case Op_LoadUS: case Op_LoadS: case Op_LoadI: case Op_LoadL: case Op_LoadF: case Op_LoadD: return Op_LoadVector;
case Op_StoreB: case Op_StoreC: case Op_StoreI: case Op_StoreL: case Op_StoreF: case Op_StoreD: return Op_StoreVector; case Op_MulAddS2I: return Op_MulAddVS2VI; case Op_CountLeadingZerosI: case Op_CountLeadingZerosL: return Op_CountLeadingZerosV; case Op_CountTrailingZerosI: case Op_CountTrailingZerosL: return Op_CountTrailingZerosV; case Op_SignumF: return Op_SignumVF; case Op_SignumD: return Op_SignumVD;
default:
assert(!VectorNode::is_convert_opcode(sopc), "Convert node %s should be processed by VectorCastNode::opcode()",
NodeClassNames[sopc]); return 0; // Unimplemented
}
}
int VectorNode::replicate_opcode(BasicType bt) { switch(bt) { case T_BOOLEAN: case T_BYTE: return Op_ReplicateB; case T_SHORT: case T_CHAR: return Op_ReplicateS; case T_INT: return Op_ReplicateI; case T_LONG: return Op_ReplicateL; case T_FLOAT: return Op_ReplicateF; case T_DOUBLE: return Op_ReplicateD; default:
assert(false, "wrong type: %s", type2name(bt)); return 0;
}
}
// Also used to check if the code generator // supports the vector operation. bool VectorNode::implemented(int opc, uint vlen, BasicType bt) { if (is_java_primitive(bt) &&
(vlen > 1) && is_power_of_2(vlen) &&
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 (VectorNode::is_vector_rotate(vopc)) { return is_vector_rotate_supported(vopc, vlen, bt);
} if (VectorNode::is_vector_integral_negate(vopc)) { return is_vector_integral_negate_supported(vopc, vlen, bt, false);
} return vopc > 0 && Matcher::match_rule_supported_superword(vopc, vlen, bt);
} returnfalse;
}
// If target defines vector rotation patterns then no // need for degeneration. if (Matcher::match_rule_supported_vector(vopc, vlen, bt)) { returntrue;
}
// If target does not support variable shift operations then no point // in creating a rotate vector node since it will not be disintegratable. // Adding a pessimistic check to avoid complex pattern matching which // may not be full proof. if (!Matcher::supports_vector_variable_shifts()) { returnfalse;
}
// 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: returnfalse;
}
}
// Check whether the architecture supports the vector negate instructions. If not, then check // whether the alternative vector nodes used to implement vector negation are supported. // Return false if neither of them is supported. bool VectorNode::is_vector_integral_negate_supported(int opc, uint vlen, BasicType bt, bool use_predicate) { if (!use_predicate) { // Check whether the NegVI/L is supported by the architecture. if (Matcher::match_rule_supported_vector(opc, vlen, bt)) { returntrue;
} // Negate is implemented with "(SubVI/L (ReplicateI/L 0) src)", if NegVI/L is not supported. int sub_opc = (bt == T_LONG) ? Op_SubL : Op_SubI; if (Matcher::match_rule_supported_vector(VectorNode::opcode(sub_opc, bt), vlen, bt) &&
Matcher::match_rule_supported_vector(VectorNode::replicate_opcode(bt), vlen, bt)) { returntrue;
}
} else { // Check whether the predicated NegVI/L is supported by the architecture. if (Matcher::match_rule_supported_vector_masked(opc, vlen, bt)) { returntrue;
} // Predicated negate is implemented with "(AddVI/L (XorV src (ReplicateI/L -1)) (ReplicateI/L 1))", // if predicated NegVI/L is not supported. int add_opc = (bt == T_LONG) ? Op_AddL : Op_AddI; if (Matcher::match_rule_supported_vector_masked(Op_XorV, vlen, bt) &&
Matcher::match_rule_supported_vector_masked(VectorNode::opcode(add_opc, bt), vlen, bt) &&
Matcher::match_rule_supported_vector(VectorNode::replicate_opcode(bt), vlen, bt)) { returntrue;
}
} returnfalse;
}
bool VectorNode::is_shift_opcode(int opc) { switch (opc) { case Op_LShiftI: case Op_LShiftL: case Op_RShiftI: case Op_RShiftL: case Op_URShiftB: case Op_URShiftS: case Op_URShiftI: case Op_URShiftL: returntrue; default: returnfalse;
}
}
bool VectorNode::can_transform_shift_op(Node* n, BasicType bt) { if (n->Opcode() != Op_URShiftI) { returnfalse;
}
Node* in2 = n->in(2); if (!in2->is_Con()) { returnfalse;
}
jint cnt = in2->get_int(); // Only when shift amount is not greater than number of sign extended // bits (16 for short and 24 for byte), unsigned shift right on signed // subword types can be vectorized as vector signed shift. if ((bt == T_BYTE && cnt <= 24) || (bt == T_SHORT && cnt <= 16)) { returntrue;
} returnfalse;
}
bool VectorNode::is_convert_opcode(int opc) { switch (opc) { case Op_ConvI2F: case Op_ConvL2D: case Op_ConvF2I: case Op_ConvD2L: case Op_ConvI2D: case Op_ConvL2F: case Op_ConvL2I: case Op_ConvI2L: case Op_ConvF2L: case Op_ConvD2F: case Op_ConvF2D: case Op_ConvD2I: case Op_ConvF2HF: case Op_ConvHF2F: returntrue; default: returnfalse;
}
}
// Check if input is loop invariant vector. bool VectorNode::is_invariant_vector(Node* n) { // Only Replicate vector nodes are loop invariant for now. switch (n->Opcode()) { case Op_ReplicateB: case Op_ReplicateS: case Op_ReplicateI: case Op_ReplicateL: case Op_ReplicateF: case Op_ReplicateD: returntrue; default: returnfalse;
}
}
// [Start, end) half-open range defining which operands are vectors void VectorNode::vector_operands(Node* n, uint* start, uint* end) { switch (n->Opcode()) { case Op_LoadB: case Op_LoadUB: case Op_LoadS: case Op_LoadUS: case Op_LoadI: case Op_LoadL: case Op_LoadF: case Op_LoadD: case Op_LoadP: case Op_LoadN:
*start = 0;
*end = 0; // no vector operands break; case Op_StoreB: case Op_StoreC: case Op_StoreI: case Op_StoreL: case Op_StoreF: case Op_StoreD: case Op_StoreP: case Op_StoreN:
*start = MemNode::ValueIn;
*end = MemNode::ValueIn + 1; // 1 vector operand break; case Op_LShiftI: case Op_LShiftL: case Op_RShiftI: case Op_RShiftL: case Op_URShiftI: case Op_URShiftL:
*start = 1;
*end = 2; // 1 vector operand break; case Op_AddI: case Op_AddL: case Op_AddF: case Op_AddD: case Op_SubI: case Op_SubL: case Op_SubF: case Op_SubD: case Op_MulI: case Op_MulL: case Op_MulF: case Op_MulD: case Op_DivF: case Op_DivD: case Op_AndI: case Op_AndL: case Op_OrI: case Op_OrL: case Op_XorI: case Op_XorL: case Op_MulAddS2I:
*start = 1;
*end = 3; // 2 vector operands break; case Op_CMoveI: case Op_CMoveL: case Op_CMoveF: case Op_CMoveD:
*start = 2;
*end = n->req(); break; case Op_FmaD: case Op_FmaF:
*start = 1;
*end = 4; // 3 vector operands break; default:
*start = 1;
*end = n->req(); // default is all operands
}
}
VectorNode* VectorNode::make_mask_node(int vopc, Node* n1, Node* n2, uint vlen, BasicType bt) {
guarantee(vopc > 0, "vopc must be > 0"); const TypeVect* vmask_type = TypeVect::makemask(bt, vlen); switch (vopc) { case Op_AndV: if (Matcher::match_rule_supported_vector_masked(Op_AndVMask, vlen, bt)) { returnnew AndVMaskNode(n1, n2, vmask_type);
} returnnew AndVNode(n1, n2, vmask_type); case Op_OrV: if (Matcher::match_rule_supported_vector_masked(Op_OrVMask, vlen, bt)) { returnnew OrVMaskNode(n1, n2, vmask_type);
} returnnew OrVNode(n1, n2, vmask_type); case Op_XorV: if (Matcher::match_rule_supported_vector_masked(Op_XorVMask, vlen, bt)) { returnnew XorVMaskNode(n1, n2, vmask_type);
} returnnew XorVNode(n1, n2, vmask_type); default:
fatal("Unsupported mask vector creation for '%s'", NodeClassNames[vopc]); return NULL;
}
}
// Make a vector node for binary operation
VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask, bool is_var_shift) { // This method should not be called for unimplemented vectors.
guarantee(vopc > 0, "vopc must be > 0");
if (is_mask) { return make_mask_node(vopc, n1, n2, vt->length(), vt->element_basic_type());
}
switch (vopc) { case Op_AddVB: returnnew AddVBNode(n1, n2, vt); case Op_AddVS: returnnew AddVSNode(n1, n2, vt); case Op_AddVI: returnnew AddVINode(n1, n2, vt); case Op_AddVL: returnnew AddVLNode(n1, n2, vt); case Op_AddVF: returnnew AddVFNode(n1, n2, vt); case Op_AddVD: returnnew AddVDNode(n1, n2, vt);
case Op_SubVB: returnnew SubVBNode(n1, n2, vt); case Op_SubVS: returnnew SubVSNode(n1, n2, vt); case Op_SubVI: returnnew SubVINode(n1, n2, vt); case Op_SubVL: returnnew SubVLNode(n1, n2, vt); case Op_SubVF: returnnew SubVFNode(n1, n2, vt); case Op_SubVD: returnnew SubVDNode(n1, n2, vt);
case Op_MulVB: returnnew MulVBNode(n1, n2, vt); case Op_MulVS: returnnew MulVSNode(n1, n2, vt); case Op_MulVI: returnnew MulVINode(n1, n2, vt); case Op_MulVL: returnnew MulVLNode(n1, n2, vt); case Op_MulVF: returnnew MulVFNode(n1, n2, vt); case Op_MulVD: returnnew MulVDNode(n1, n2, vt);
case Op_DivVF: returnnew DivVFNode(n1, n2, vt); case Op_DivVD: returnnew DivVDNode(n1, n2, vt);
case Op_MinV: returnnew MinVNode(n1, n2, vt); case Op_MaxV: returnnew MaxVNode(n1, n2, vt);
case Op_AbsVF: returnnew AbsVFNode(n1, vt); case Op_AbsVD: returnnew AbsVDNode(n1, vt); case Op_AbsVB: returnnew AbsVBNode(n1, vt); case Op_AbsVS: returnnew AbsVSNode(n1, vt); case Op_AbsVI: returnnew AbsVINode(n1, vt); case Op_AbsVL: returnnew AbsVLNode(n1, vt);
case Op_NegVI: returnnew NegVINode(n1, vt); case Op_NegVL: returnnew NegVLNode(n1, vt); case Op_NegVF: returnnew NegVFNode(n1, vt); case Op_NegVD: returnnew NegVDNode(n1, vt);
case Op_ReverseV: returnnew ReverseVNode(n1, vt); case Op_ReverseBytesV: returnnew ReverseBytesVNode(n1, vt);
case Op_SqrtVF: returnnew SqrtVFNode(n1, vt); case Op_SqrtVD: returnnew SqrtVDNode(n1, vt);
case Op_RoundVF: returnnew RoundVFNode(n1, vt); case Op_RoundVD: returnnew RoundVDNode(n1, vt);
case Op_PopCountVI: returnnew PopCountVINode(n1, vt); case Op_PopCountVL: returnnew PopCountVLNode(n1, vt); case Op_RotateLeftV: returnnew RotateLeftVNode(n1, n2, vt); case Op_RotateRightV: returnnew RotateRightVNode(n1, n2, vt);
case Op_LShiftVB: returnnew LShiftVBNode(n1, n2, vt, is_var_shift); case Op_LShiftVS: returnnew LShiftVSNode(n1, n2, vt, is_var_shift); case Op_LShiftVI: returnnew LShiftVINode(n1, n2, vt, is_var_shift); case Op_LShiftVL: returnnew LShiftVLNode(n1, n2, vt, is_var_shift);
case Op_RShiftVB: returnnew RShiftVBNode(n1, n2, vt, is_var_shift); case Op_RShiftVS: returnnew RShiftVSNode(n1, n2, vt, is_var_shift); case Op_RShiftVI: returnnew RShiftVINode(n1, n2, vt, is_var_shift); case Op_RShiftVL: returnnew RShiftVLNode(n1, n2, vt, is_var_shift);
case Op_URShiftVB: returnnew URShiftVBNode(n1, n2, vt, is_var_shift); case Op_URShiftVS: returnnew URShiftVSNode(n1, n2, vt, is_var_shift); case Op_URShiftVI: returnnew URShiftVINode(n1, n2, vt, is_var_shift); case Op_URShiftVL: returnnew URShiftVLNode(n1, n2, vt, is_var_shift);
case Op_AndV: returnnew AndVNode(n1, n2, vt); case Op_OrV: returnnew OrVNode (n1, n2, vt); case Op_XorV: returnnew XorVNode(n1, n2, vt);
case Op_RoundDoubleModeV: returnnew RoundDoubleModeVNode(n1, n2, vt);
case Op_MulAddVS2VI: returnnew MulAddVS2VINode(n1, n2, vt);
case Op_ExpandV: returnnew ExpandVNode(n1, n2, vt); case Op_CompressV: returnnew CompressVNode(n1, n2, vt); case Op_CompressM: assert(n1 == NULL, ""); returnnew CompressMNode(n2, vt); case Op_CountLeadingZerosV: returnnew CountLeadingZerosVNode(n1, vt); case Op_CountTrailingZerosV: returnnew CountTrailingZerosVNode(n1, vt); default:
fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); return NULL;
}
}
// Return the vector version of a scalar binary operation node.
VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt, bool is_var_shift) { const TypeVect* vt = TypeVect::make(bt, vlen); int vopc = VectorNode::opcode(opc, bt); // This method should not be called for unimplemented vectors.
guarantee(vopc > 0, "Vector for '%s' is not implemented", NodeClassNames[opc]); return make(vopc, n1, n2, vt, false, is_var_shift);
}
// Make a vector node for ternary operation
VectorNode* VectorNode::make(int vopc, Node* n1, Node* n2, Node* n3, const TypeVect* vt) { // This method should not be called for unimplemented vectors.
guarantee(vopc > 0, "vopc must be > 0"); switch (vopc) { case Op_FmaVD: returnnew FmaVDNode(n1, n2, n3, vt); case Op_FmaVF: returnnew FmaVFNode(n1, n2, n3, vt); case Op_SignumVD: returnnew SignumVDNode(n1, n2, n3, vt); case Op_SignumVF: returnnew SignumVFNode(n1, n2, n3, vt); default:
fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); return NULL;
}
}
// Return the vector version of a scalar ternary operation node.
VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); int vopc = VectorNode::opcode(opc, bt); // This method should not be called for unimplemented vectors.
guarantee(vopc > 0, "Vector for '%s' is not implemented", NodeClassNames[opc]); return make(vopc, n1, n2, n3, vt);
}
bool VectorNode::is_vector_shift(int opc) {
assert(opc > _last_machine_leaf && opc < _last_opcode, "invalid opcode"); switch (opc) { case Op_LShiftVB: case Op_LShiftVS: case Op_LShiftVI: case Op_LShiftVL: case Op_RShiftVB: case Op_RShiftVS: case Op_RShiftVI: case Op_RShiftVL: case Op_URShiftVB: case Op_URShiftVS: case Op_URShiftVI: case Op_URShiftVL: returntrue; default: returnfalse;
}
}
staticbool is_con(Node* n, long con) { if (n->is_Con()) { const Type* t = n->bottom_type(); if (t->isa_int() && t->is_int()->get_con() == (int)con) { returntrue;
} if (t->isa_long() && t->is_long()->get_con() == con) { returntrue;
}
} returnfalse;
}
// Return true if every bit in this vector is 1. bool VectorNode::is_all_ones_vector(Node* n) { switch (n->Opcode()) { case Op_ReplicateB: case Op_ReplicateS: case Op_ReplicateI: case Op_ReplicateL: case Op_MaskAll: return is_con(n->in(1), -1); default: returnfalse;
}
}
// Return true if every bit in this vector is 0. bool VectorNode::is_all_zeros_vector(Node* n) { switch (n->Opcode()) { case Op_ReplicateB: case Op_ReplicateS: case Op_ReplicateI: case Op_ReplicateL: case Op_MaskAll: return is_con(n->in(1), 0); default: returnfalse;
}
}
// Predicated vectors do not need to add another mask input if (node->is_predicated_vector() || !Matcher::has_predicated_vectors() ||
!Matcher::match_rule_supported_vector_masked(vopc, vlen, bt) ||
!Matcher::match_rule_supported_vector(Op_VectorMaskGen, vlen, bt)) { return NULL;
}
Node* mask = NULL; // Generate a vector mask for vector operation whose vector length is lower than the // hardware supported max vector length. if (vt->length_in_bytes() < (uint)MaxVectorSize) {
Node* length = gvn->transform(new ConvI2LNode(gvn->makecon(TypeInt::make(vlen))));
mask = gvn->transform(VectorMaskGenNode::make(length, bt, vlen));
} else { return NULL;
}
// Generate the related masked op for vector load/store/load_gather/store_scatter. // Or append the mask to the vector op's input list by default. switch(vopc) { case Op_LoadVector: returnnew LoadVectorMaskedNode(node->in(0), node->in(1), node->in(2),
node->as_LoadVector()->adr_type(), vt, mask,
node->as_LoadVector()->control_dependency()); case Op_LoadVectorGather: returnnew LoadVectorGatherMaskedNode(node->in(0), node->in(1), node->in(2),
node->as_LoadVector()->adr_type(), vt,
node->in(3), mask); case Op_StoreVector: returnnew StoreVectorMaskedNode(node->in(0), node->in(1), node->in(2), node->in(3),
node->as_StoreVector()->adr_type(), mask); case Op_StoreVectorScatter: returnnew StoreVectorScatterMaskedNode(node->in(0), node->in(1), node->in(2),
node->as_StoreVector()->adr_type(),
node->in(3), node->in(4), mask); default: // Add the mask as an additional input to the original vector node by default. // This is used for almost all the vector nodes.
node->add_req(mask);
node->add_flag(Node::Flag_is_predicated_vector); return node;
}
}
// Return initial Pack node. Additional operands added with add_opd() calls.
PackNode* PackNode::make(Node* s, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); switch (bt) { case T_BOOLEAN: case T_BYTE: returnnew PackBNode(s, vt); case T_CHAR: case T_SHORT: returnnew PackSNode(s, vt); case T_INT: returnnew PackINode(s, vt); case T_LONG: returnnew PackLNode(s, vt); case T_FLOAT: returnnew PackFNode(s, vt); case T_DOUBLE: returnnew PackDNode(s, vt); default:
fatal("Type '%s' is not supported for vectors", type2name(bt)); return NULL;
}
}
// Create a binary tree form for Packs. [lo, hi) (half-open) range
PackNode* PackNode::binary_tree_pack(int lo, int hi) { int ct = hi - lo;
assert(is_power_of_2(ct), "power of 2"); if (ct == 2) {
PackNode* pk = PackNode::make(in(lo), 2, vect_type()->element_basic_type());
pk->add_opd(in(lo+1)); return pk;
} else { int mid = lo + ct/2;
PackNode* n1 = binary_tree_pack(lo, mid);
PackNode* n2 = binary_tree_pack(mid, hi );
BasicType bt = n1->vect_type()->element_basic_type();
assert(bt == n2->vect_type()->element_basic_type(), "should be the same"); switch (bt) { case T_BOOLEAN: case T_BYTE: returnnew PackSNode(n1, n2, TypeVect::make(T_SHORT, 2)); case T_CHAR: case T_SHORT: returnnew PackINode(n1, n2, TypeVect::make(T_INT, 2)); case T_INT: returnnew PackLNode(n1, n2, TypeVect::make(T_LONG, 2)); case T_LONG: returnnew Pack2LNode(n1, n2, TypeVect::make(T_LONG, 2)); case T_FLOAT: returnnew PackDNode(n1, n2, TypeVect::make(T_DOUBLE, 2)); case T_DOUBLE: returnnew Pack2DNode(n1, n2, TypeVect::make(T_DOUBLE, 2)); default:
fatal("Type '%s' is not supported for vectors", type2name(bt)); return NULL;
}
}
}
// Return the vector version of a scalar load node.
LoadVectorNode* LoadVectorNode::make(int opc, Node* ctl, Node* mem,
Node* adr, const TypePtr* atyp,
uint vlen, BasicType bt,
ControlDependency control_dependency) { const TypeVect* vt = TypeVect::make(bt, vlen); returnnew LoadVectorNode(ctl, mem, adr, atyp, vt, control_dependency);
}
// Return the vector version of a scalar store node.
StoreVectorNode* StoreVectorNode::make(int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, Node* val, uint vlen) { returnnew StoreVectorNode(ctl, mem, adr, atyp, val);
}
Node* ReductionNode::make_reduction_input(PhaseGVN& gvn, int opc, BasicType bt) { int vopc = opcode(opc, bt);
guarantee(vopc != opc, "Vector reduction for '%s' is not implemented", NodeClassNames[opc]);
switch (vopc) { case Op_AndReductionV: switch (bt) { case T_BYTE: case T_SHORT: case T_INT: return gvn.makecon(TypeInt::MINUS_1); case T_LONG: return gvn.makecon(TypeLong::MINUS_1); default:
fatal("Missed vector creation for '%s' as the basic type is not correct.", NodeClassNames[vopc]); return NULL;
} break; case Op_AddReductionVI: // fallthrough case Op_AddReductionVL: // fallthrough case Op_AddReductionVF: // fallthrough case Op_AddReductionVD: case Op_OrReductionV: case Op_XorReductionV: return gvn.zerocon(bt); case Op_MulReductionVI: return gvn.makecon(TypeInt::ONE); case Op_MulReductionVL: return gvn.makecon(TypeLong::ONE); case Op_MulReductionVF: return gvn.makecon(TypeF::ONE); case Op_MulReductionVD: return gvn.makecon(TypeD::ONE); case Op_MinReductionV: switch (bt) { case T_BYTE: return gvn.makecon(TypeInt::make(max_jbyte)); case T_SHORT: return gvn.makecon(TypeInt::make(max_jshort)); case T_INT: return gvn.makecon(TypeInt::MAX); case T_LONG: return gvn.makecon(TypeLong::MAX); case T_FLOAT: return gvn.makecon(TypeF::POS_INF); case T_DOUBLE: return gvn.makecon(TypeD::POS_INF); default: Unimplemented(); return NULL;
} break; case Op_MaxReductionV: switch (bt) { case T_BYTE: return gvn.makecon(TypeInt::make(min_jbyte)); case T_SHORT: return gvn.makecon(TypeInt::make(min_jshort)); case T_INT: return gvn.makecon(TypeInt::MIN); case T_LONG: return gvn.makecon(TypeLong::MIN); case T_FLOAT: return gvn.makecon(TypeF::NEG_INF); case T_DOUBLE: return gvn.makecon(TypeD::NEG_INF); default: Unimplemented(); return NULL;
} break; default:
fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); return NULL;
}
}
Node* VectorReinterpretNode::Identity(PhaseGVN *phase) {
Node* n = in(1); if (n->Opcode() == Op_VectorReinterpret) { // "VectorReinterpret (VectorReinterpret node) ==> node" if: // 1) Types of 'node' and 'this' are identical // 2) Truncations are not introduced by the first VectorReinterpret if (Type::cmp(bottom_type(), n->in(1)->bottom_type()) == 0 &&
length_in_bytes() <= n->bottom_type()->is_vect()->length_in_bytes()) { return n->in(1);
}
} returnthis;
}
static Node* reverse_operations_identity(Node* n, Node* in1) { if (n->is_predicated_using_blend()) { return n;
} if (n->Opcode() == in1->Opcode()) { // OperationV (OperationV X MASK) MASK => X if (n->is_predicated_vector() && in1->is_predicated_vector() && n->in(2) == in1->in(2)) { return in1->in(1); // OperationV (OperationV X) => X
} elseif (!n->is_predicated_vector() && !in1->is_predicated_vector()) { return in1->in(1);
}
} return n;
}
Node* ReverseBytesVNode::Identity(PhaseGVN* phase) { // "(ReverseBytesV X) => X" if the element type is T_BYTE. if (vect_type()->element_basic_type() == T_BYTE) { return in(1);
} return reverse_operations_identity(this, in(1));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.