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
24 changes: 20 additions & 4 deletions src/hotspot/share/opto/superword.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2309,7 +2309,7 @@ bool SuperWord::is_vector_use(Node* use, int u_idx) const {
return true;
}

if (!is_velt_basic_type_compatible_use_def(use, def)) {
if (!is_velt_basic_type_compatible_use_def(use, def, d_pk->size())) {
return false;
}

Expand Down Expand Up @@ -2375,7 +2375,7 @@ Node_List* PackSet::strided_pack_input_at_index_or_null(const Node_List* pack, c

// Check if the output type of def is compatible with the input type of use, i.e. if the
// types have the same size.
bool SuperWord::is_velt_basic_type_compatible_use_def(Node* use, Node* def) const {
bool SuperWord::is_velt_basic_type_compatible_use_def(Node* use, Node* def, const uint pack_size) const {
assert(in_bb(def) && in_bb(use), "both use and def are in loop");

// Conversions are trivially compatible.
Expand All @@ -2401,8 +2401,24 @@ bool SuperWord::is_velt_basic_type_compatible_use_def(Node* use, Node* def) cons
type2aelembytes(use_bt) == 4;
}

// Default case: input size of use equals output size of def.
return type2aelembytes(use_bt) == type2aelembytes(def_bt);
// Input size of use equals output size of def
if (type2aelembytes(use_bt) == type2aelembytes(def_bt)) {
return true;
}

// Subword cast: Element sizes differ, but the platform supports a cast to change the def shape to the use shape.
if (is_supported_subword_cast(def_bt, use_bt, pack_size)) {
return true;
}

return false;
}

bool SuperWord::is_supported_subword_cast(BasicType def_bt, BasicType use_bt, const uint pack_size) {
assert(def_bt != use_bt, "use and def types must be different");

// Opcode is only required to disambiguate half float, so we pass -1 as it can't be encountered here.
return (is_subword_type(def_bt) || is_subword_type(use_bt)) && VectorCastNode::implemented(-1, pack_size, def_bt, use_bt);
}
Comment on lines +2417 to 2422
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we discussed this before: should we not move this to VectorCastNode, rather than having it in SuperWord?


// Return nullptr if success, else failure message
Expand Down
5 changes: 4 additions & 1 deletion src/hotspot/share/opto/superword.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,9 +653,12 @@ class SuperWord : public ResourceObj {
// Is use->in(u_idx) a vector use?
bool is_vector_use(Node* use, int u_idx) const;

bool is_velt_basic_type_compatible_use_def(Node* use, Node* def) const;
bool is_velt_basic_type_compatible_use_def(Node* use, Node* def, const uint pack_size) const;

bool schedule_and_apply() const;

public:
static bool is_supported_subword_cast(BasicType def_bt, BasicType use_bt, const uint pack_size);
};

#endif // SHARE_OPTO_SUPERWORD_HPP
13 changes: 13 additions & 0 deletions src/hotspot/share/opto/superwordVTransformBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,19 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i

Node_List* pack_in = _packset.pack_input_at_index_or_null(pack, index);
if (pack_in != nullptr) {
Node* in_p0 = pack_in->at(0);
BasicType def_bt = _vloop_analyzer.types().velt_basic_type(in_p0);
BasicType use_bt = _vloop_analyzer.types().velt_basic_type(p0);

// If the use and def types are different, emit a cast node
if (use_bt != def_bt && !p0->is_Convert() && SuperWord::is_supported_subword_cast(def_bt, use_bt, pack->size())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is SuperWord::is_supported_subword_cast(def_bt, use_bt, pack->size()) really a true condition that you need to check here (and if false we can continue in the "else"), or should it be rather an assert?

VTransformNode* in = get_vtnode(pack_in->at(0));
VTransformNode* cast = new (_vtransform.arena()) VTransformCastVectorNode(_vtransform, pack->size(), def_bt, use_bt);
cast->set_req(1, in);

return cast;
}

// Input is a matching pack -> vtnode already exists.
assert(index != 2 || !VectorNode::is_shift(p0), "shift's count cannot be vector");
return get_vtnode(pack_in->at(0));
Expand Down
13 changes: 13 additions & 0 deletions src/hotspot/share/opto/vtransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,15 @@ VTransformApplyResult VTransformStoreVectorNode::apply(const VLoopAnalyzer& vloo
return VTransformApplyResult::make_vector(vn, vlen, vn->memory_size());
}

VTransformApplyResult VTransformCastVectorNode::apply(const VLoopAnalyzer& vloop_analyzer,
const GrowableArray<Node*>& vnode_idx_to_transformed_node) const {
Node* value = find_transformed_input(1, vnode_idx_to_transformed_node);
VectorNode* vn = VectorCastNode::make(VectorCastNode::opcode(-1, _from_bt), value, _to_bt, _vlen);
register_new_node_from_vectorization(vloop_analyzer, vn, value);

return VTransformApplyResult::make_vector(vn, _vlen, vn->vect_type()->length_in_bytes());
}

void VTransformVectorNode::register_new_node_from_vectorization_and_replace_scalar_nodes(const VLoopAnalyzer& vloop_analyzer, Node* vn) const {
PhaseIdealLoop* phase = vloop_analyzer.vloop().phase();
Node* first = nodes().at(0);
Expand Down Expand Up @@ -790,6 +799,10 @@ void VTransformPopulateIndexNode::print_spec() const {
tty->print("vlen=%d element_bt=%s", _vlen, type2name(_element_bt));
}

void VTransformCastVectorNode::print_spec() const {
tty->print("vlen=%d from=%s to=%s", _vlen, type2name(_from_bt), type2name(_to_bt));
}

void VTransformVectorNode::print_spec() const {
tty->print("%d-pack[", _nodes.length());
for (int i = 0; i < _nodes.length(); i++) {
Expand Down
16 changes: 16 additions & 0 deletions src/hotspot/share/opto/vtransform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,22 @@ class VTransformStoreVectorNode : public VTransformMemVectorNode {
NOT_PRODUCT(virtual const char* name() const override { return "StoreVector"; };)
};

class VTransformCastVectorNode : public VTransformNode {
private:
uint _vlen;
BasicType _from_bt;
BasicType _to_bt;

public:
// req = 2 -> [ctrl, input]
VTransformCastVectorNode(VTransform& vtransform, int vlen, BasicType from_bt, BasicType to_bt) : VTransformNode(vtransform, 2),
_vlen(vlen), _from_bt(from_bt), _to_bt(to_bt) {}
virtual VTransformApplyResult apply(const VLoopAnalyzer& vloop_analyzer,
const GrowableArray<Node*>& vnode_idx_to_transformed_node) const override;
NOT_PRODUCT(virtual const char* name() const override { return "CastVector"; };)
NOT_PRODUCT(virtual void print_spec() const override;)
};

// Invoke callback on all memops, in the order of the schedule.
template<typename Callback>
void VTransformGraph::for_each_memop_in_schedule(Callback callback) const {
Expand Down
14 changes: 7 additions & 7 deletions test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public class TestMinMaxSubword {
}
}

// Ensure vector max/min instructions are not generated for integer subword types
// as Java APIs for Math.min/max do not support integer subword types and superword
// should not generate vectorized Min/Max nodes for them.
// Ensure that casts to/from subword types are emitted, as java APIs for Math.min/max do not support integer subword
// types and superword should generate int versions and then cast between them.

@Test
@IR(failOn = {IRNode.MIN_VI, IRNode.MIN_VF, IRNode.MIN_VD})
@IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE_ANY, ">0" })
public static void testMinShort() {
for (int i = 0; i < LENGTH; i++) {
sb[i] = (short) Math.min(sa[i], val);
Expand All @@ -77,7 +77,7 @@ public static void testMinShort_runner() {
}

@Test
@IR(failOn = {IRNode.MAX_VI, IRNode.MAX_VF, IRNode.MAX_VD})
@IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE_ANY, ">0" })
public static void testMaxShort() {
for (int i = 0; i < LENGTH; i++) {
sb[i] = (short) Math.max(sa[i], val);
Expand All @@ -92,7 +92,7 @@ public static void testMaxShort_runner() {
}

@Test
@IR(failOn = {IRNode.MIN_VI, IRNode.MIN_VF, IRNode.MIN_VD})
@IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE_ANY, ">0" })
public static void testMinByte() {
for (int i = 0; i < LENGTH; i++) {
bb[i] = (byte) Math.min(ba[i], val);
Expand All @@ -108,7 +108,7 @@ public static void testMinByte_runner() {
}

@Test
@IR(failOn = {IRNode.MAX_VI, IRNode.MAX_VF, IRNode.MAX_VD})
@IR(applyIfCPUFeature = { "avx", "true" }, counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE_ANY, ">0" })
public static void testMaxByte() {
for (int i = 0; i < LENGTH; i++) {
bb[i] = (byte) Math.max(ba[i], val);
Expand Down
Loading