Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/hotspot/share/opto/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,9 @@ bool Node::has_special_unique_user() const {
} else if ((is_IfFalse() || is_IfTrue()) && n->is_If()) {
// See IfNode::fold_compares
return true;
} else if (n->Opcode() == Op_XorV || n->Opcode() == Op_XorVMask) {
// Condition for XorVMask(VectorMaskCmp(x,y,cond), MaskAll(true)) ==> VectorMaskCmp(x,y,ncond)
return true;
} else {
return false;
}
Expand Down
4 changes: 3 additions & 1 deletion src/hotspot/share/opto/subnode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,9 @@ struct BoolTest {
// a simple char array where each element is the ASCII version of a 'mask'
// enum from above.
mask commute( ) const { return mask("032147658"[_test]-'0'); }
mask negate( ) const { return mask(_test^4); }
mask negate( ) const { return negate_mask(_test); }
// Return the negative mask for the given mask, for both signed and unsigned comparison.
static mask negate_mask(mask btm) { return mask(btm ^ 4); }
bool is_canonical( ) const { return (_test == BoolTest::ne || _test == BoolTest::lt || _test == BoolTest::le || _test == BoolTest::overflow); }
bool is_less( ) const { return _test == BoolTest::lt || _test == BoolTest::le; }
bool is_greater( ) const { return _test == BoolTest::gt || _test == BoolTest::ge; }
Expand Down
98 changes: 98 additions & 0 deletions src/hotspot/share/opto/vectornode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2268,6 +2268,99 @@ Node* OrVNode::Identity(PhaseGVN* phase) {
return redundant_logical_identity(this);
}

// Returns whether (XorV (VectorMaskCmp) -1) can be optimized by negating the
// comparison operation.
bool VectorMaskCmpNode::predicate_can_be_negated() {
switch (_predicate) {
case BoolTest::eq:
case BoolTest::ne:
// eq and ne also apply to floating-point special values like NaN and infinities.
return true;
case BoolTest::le:
case BoolTest::ge:
case BoolTest::lt:
case BoolTest::gt:
case BoolTest::ule:
case BoolTest::uge:
case BoolTest::ult:
case BoolTest::ugt: {
BasicType bt = vect_type()->element_basic_type();
// For float and double, we don't know if either comparison operand is a
// NaN, NaN {le|ge|lt|gt} anything is false, resulting in inconsistent
// results before and after negation.
return is_integral_type(bt);
}
default:
return false;
}
}

// This function transforms the following patterns:
//
// For integer types:
// (XorV (VectorMaskCmp src1 src2 cond) (Replicate -1))
// => (VectorMaskCmp src1 src2 ncond)
// (XorVMask (VectorMaskCmp src1 src2 cond) (MaskAll m1))
// => (VectorMaskCmp src1 src2 ncond)
// (XorV (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (Replicate -1))
// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond))
// (XorVMask (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (MaskAll m1))
// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond))
// cond can be eq, ne, le, ge, lt, gt, ule, uge, ult and ugt.
// ncond is the negative comparison of cond.
//
// For float and double types:
// (XorV (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (Replicate -1))
// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond))
// (XorVMask (VectorMaskCast (VectorMaskCmp src1 src2 cond)) (MaskAll m1))
// => (VectorMaskCast (VectorMaskCmp src1 src2 ncond))
// cond can be eq or ne.
Node* XorVNode::Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape) {
Node* in1 = in(1);
Node* in2 = in(2);
// Transformations for predicated vectors are not supported for now.
if (is_predicated_vector() ||
in1->is_predicated_vector() ||
in2->is_predicated_vector()) {
return nullptr;
}

// XorV/XorVMask is commutative, swap VectorMaskCmp/VectorMaskCast to in1.
if (VectorNode::is_all_ones_vector(in1)) {
swap(in1, in2);
}

bool with_vector_mask_cast = false;
// Required conditions:
// 1. VectorMaskCast and VectorMaskCmp should only have a single use,
// otherwise the optimization may be unprofitable.
// 2. The predicate of VectorMaskCmp should be negatable.
// 3. The second input should be an all true vector mask.
if (in1->Opcode() == Op_VectorMaskCast) {
if (in1->outcnt() != 1) {
return nullptr;
}
with_vector_mask_cast = true;
in1 = in1->in(1);
}
if (in1->Opcode() != Op_VectorMaskCmp ||
in1->outcnt() != 1 ||
!in1->as_VectorMaskCmp()->predicate_can_be_negated() ||
!VectorNode::is_all_ones_vector(in2)) {
return nullptr;
}

BoolTest::mask neg_cond = BoolTest::negate_mask((in1->as_VectorMaskCmp())->get_predicate());
ConINode* predicate_node = phase->intcon(neg_cond);
const TypeVect* vt = in1->as_Vector()->vect_type();
Node* res = new VectorMaskCmpNode(neg_cond, in1->in(1), in1->in(2), predicate_node, vt);
if (with_vector_mask_cast) {
// We optimized out a VectorMaskCast, regenerate one to ensure type correctness.
res = new VectorMaskCastNode(phase->transform(res), vect_type());
}
return res;
}

Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) {
// (XorV src src) => (Replicate zero)
// (XorVMask src src) => (MaskAll zero)
Expand All @@ -2281,6 +2374,11 @@ Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* zero = phase->transform(phase->zerocon(bt));
return VectorNode::scalar2vector(zero, length(), bt, bottom_type()->isa_vectmask() != nullptr);
}

Node* res = Ideal_XorV_VectorMaskCmp(phase, can_reshape);
if (res != nullptr) {
return res;
}
return VectorNode::Ideal(phase, can_reshape);
}

Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/opto/vectornode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,7 @@ class XorVNode : public VectorNode {
XorVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {}
virtual int Opcode() const;
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
Node* Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape);
};

//------------------------------XorReductionVNode--------------------------------------
Expand Down Expand Up @@ -1676,6 +1677,7 @@ class VectorMaskCmpNode : public VectorNode {
virtual bool cmp( const Node &n ) const {
return VectorNode::cmp(n) && _predicate == ((VectorMaskCmpNode&)n)._predicate;
}
bool predicate_can_be_negated();
BoolTest::mask get_predicate() { return _predicate; }
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;
Expand Down
10 changes: 10 additions & 0 deletions test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2295,6 +2295,11 @@ public class IRNode {
vectorNode(VECTOR_MASK_CMP_D, "VectorMaskCmp", TYPE_DOUBLE);
}

public static final String VECTOR_MASK_CMP = PREFIX + "VECTOR_MASK_CMP" + POSTFIX;
static {
beforeMatchingNameRegex(VECTOR_MASK_CMP, "VectorMaskCmp");
}

public static final String VECTOR_CAST_B2S = VECTOR_PREFIX + "VECTOR_CAST_B2S" + POSTFIX;
static {
vectorNode(VECTOR_CAST_B2S, "VectorCastB2X", TYPE_SHORT);
Expand Down Expand Up @@ -2705,6 +2710,11 @@ public class IRNode {
vectorNode(XOR_VL, "XorV", TYPE_LONG);
}

public static final String XOR_V = PREFIX + "XOR_V" + POSTFIX;
static {
beforeMatchingNameRegex(XOR_V, "XorV");
}

public static final String XOR_V_MASK = PREFIX + "XOR_V_MASK" + POSTFIX;
static {
beforeMatchingNameRegex(XOR_V_MASK, "XorVMask");
Expand Down
Loading