Skip to content

Commit 436d291

Browse files
Eric FangXiaohong Gong
authored andcommitted
8370863: VectorAPI: Optimize the VectorMaskCast chain in specific patterns
Reviewed-by: xgong, vlivanov, galder
1 parent 29024c2 commit 436d291

9 files changed

Lines changed: 652 additions & 119 deletions

File tree

src/hotspot/share/opto/phaseX.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2123,7 +2123,12 @@ void PhaseIterGVN::verify_Identity_for(Node* n) {
21232123

21242124
if (n->is_Vector()) {
21252125
// Found with tier1-3. Not investigated yet.
2126-
// The observed issue was with AndVNode::Identity
2126+
// The observed issue was with AndVNode::Identity and
2127+
// VectorStoreMaskNode::Identity (see JDK-8370863).
2128+
//
2129+
// Found with:
2130+
// compiler/vectorapi/VectorStoreMaskIdentityTest.java
2131+
// -XX:CompileThreshold=100 -XX:-TieredCompilation -XX:VerifyIterativeGVN=1110
21272132
return;
21282133
}
21292134

src/hotspot/share/opto/vectornode.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,20 @@ Node* VectorNode::Ideal(PhaseGVN* phase, bool can_reshape) {
10471047
return nullptr;
10481048
}
10491049

1050+
// Traverses a chain of VectorMaskCast and returns the first non VectorMaskCast node.
1051+
//
1052+
// Due to the unique nature of vector masks, for specific IR patterns,
1053+
// VectorMaskCast does not affect the output results. For example:
1054+
// (VectorStoreMask (VectorMaskCast* (VectorLoadMask x))) => (x)
1055+
// x remains to be a bool vector with no changes.
1056+
// This function can be used to eliminate the VectorMaskCast in such patterns.
1057+
Node* VectorNode::uncast_mask(Node* n) {
1058+
while (n->Opcode() == Op_VectorMaskCast) {
1059+
n = n->in(1);
1060+
}
1061+
return n;
1062+
}
1063+
10501064
// Return initial Pack node. Additional operands added with add_opd() calls.
10511065
PackNode* PackNode::make(Node* s, uint vlen, BasicType bt) {
10521066
const TypeVect* vt = TypeVect::make(bt, vlen);
@@ -1495,10 +1509,12 @@ Node* VectorLoadMaskNode::Identity(PhaseGVN* phase) {
14951509

14961510
Node* VectorStoreMaskNode::Identity(PhaseGVN* phase) {
14971511
// Identity transformation on boolean vectors.
1498-
// VectorStoreMask (VectorLoadMask bv) elem_size ==> bv
1512+
// VectorStoreMask (VectorMaskCast* VectorLoadMask bv) elem_size ==> bv
14991513
// vector[n]{bool} => vector[n]{t} => vector[n]{bool}
1500-
if (in(1)->Opcode() == Op_VectorLoadMask) {
1501-
return in(1)->in(1);
1514+
Node* in1 = VectorNode::uncast_mask(in(1));
1515+
if (in1->Opcode() == Op_VectorLoadMask) {
1516+
assert(length() == in1->as_Vector()->length(), "vector length must match");
1517+
return in1->in(1);
15021518
}
15031519
return this;
15041520
}
@@ -1959,11 +1975,12 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) {
19591975
}
19601976

19611977
Node* VectorMaskCastNode::Identity(PhaseGVN* phase) {
1962-
Node* in1 = in(1);
1963-
// VectorMaskCast (VectorMaskCast x) => x
1964-
if (in1->Opcode() == Op_VectorMaskCast &&
1965-
vect_type()->eq(in1->in(1)->bottom_type())) {
1966-
return in1->in(1);
1978+
// (VectorMaskCast+ x) => (x)
1979+
// If the types of the input and output nodes in a VectorMaskCast chain are
1980+
// exactly the same, the intermediate VectorMaskCast nodes can be eliminated.
1981+
Node* n = VectorNode::uncast_mask(this);
1982+
if (vect_type()->eq(n->bottom_type())) {
1983+
return n;
19671984
}
19681985
return this;
19691986
}

src/hotspot/share/opto/vectornode.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class VectorNode : public TypeNode {
195195
static bool is_scalar_op_that_returns_int_but_vector_op_returns_long(int opc);
196196
static bool is_reinterpret_opcode(int opc);
197197

198+
static Node* uncast_mask(Node* n);
198199

199200
static void trace_new_vector(Node* n, const char* context) {
200201
#ifdef ASSERT

test/hotspot/jtreg/compiler/vectorapi/TestVectorLongToMaskNodeIdealization.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
/*
4848
* @test
49-
* @bug 8277997 8378968
49+
* @bug 8277997 8378968 8380290
5050
* @key randomness
5151
* @summary Testing some optimizations in VectorLongToMaskNode::Ideal
5252
* For now: VectorMask.fromLong(.., mask.toLong())
@@ -93,7 +93,7 @@ public static void main(String[] args) {
9393
IRNode.VECTOR_STORE_MASK, "> 0", // Not yet optimized away
9494
IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away
9595
IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away
96-
IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized away
96+
IRNode.VECTOR_MASK_CAST, "= 0", // Optimized away
9797
IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0",
9898
IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0",
9999
IRNode.STORE_VECTOR, "> 0"},
@@ -168,7 +168,7 @@ public static void check_test1(Object out) {
168168
IRNode.VECTOR_STORE_MASK, "> 0", // Not yet optimized away
169169
IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away
170170
IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away
171-
IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized away: Cast Z->Z, see JDK-8379866
171+
IRNode.VECTOR_MASK_CAST, "= 0", // Optimized away
172172
IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0",
173173
IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0",
174174
IRNode.STORE_VECTOR, "> 0"},
@@ -219,7 +219,7 @@ public static void check_test1b(Object out) {
219219
IRNode.VECTOR_STORE_MASK, "= 0",
220220
IRNode.VECTOR_LONG_TO_MASK, "= 0", // Optimized away
221221
IRNode.VECTOR_MASK_TO_LONG, "= 0", // Optimized away
222-
IRNode.VECTOR_MASK_CAST, "> 0", // Not yet optimized Z->Z, see JDK-8379866
222+
IRNode.VECTOR_MASK_CAST, "= 0", // Optimized away
223223
IRNode.VECTOR_BLEND_I, IRNode.VECTOR_SIZE_4, "> 0",
224224
IRNode.XOR_VI, IRNode.VECTOR_SIZE_4, "> 0",
225225
IRNode.STORE_VECTOR, "> 0"},

test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
* Copyright (c) 2025, 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,10 +23,10 @@
2323

2424
/*
2525
* @test
26-
* @bug 8356760
26+
* @bug 8356760 8370863
2727
* @key randomness
2828
* @library /test/lib /
29-
* @summary Optimize VectorMask.fromLong for all-true/all-false cases
29+
* @summary test VectorMaskCast Identity() optimizations
3030
* @modules jdk.incubator.vector
3131
*
3232
* @run driver compiler.vectorapi.VectorMaskCastIdentityTest
@@ -50,70 +50,88 @@ public class VectorMaskCastIdentityTest {
5050
}
5151

5252
@Test
53-
@IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"asimd", "true", "rvv", "true"})
54-
public static int testTwoCastToDifferentType() {
55-
// The types before and after the two casts are not the same, so the cast cannot be eliminated.
56-
VectorMask<Float> mFloat64 = VectorMask.fromArray(FloatVector.SPECIES_64, mr, 0);
57-
VectorMask<Double> mDouble128 = mFloat64.cast(DoubleVector.SPECIES_128);
58-
VectorMask<Integer> mInt64 = mDouble128.cast(IntVector.SPECIES_64);
59-
return mInt64.trueCount();
60-
}
61-
62-
@Run(test = "testTwoCastToDifferentType")
63-
public static void testTwoCastToDifferentType_runner() {
64-
int count = testTwoCastToDifferentType();
65-
VectorMask<Float> mFloat64 = VectorMask.fromArray(FloatVector.SPECIES_64, mr, 0);
66-
Asserts.assertEquals(count, mFloat64.trueCount());
67-
}
68-
69-
@Test
70-
@IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"avx2", "true"})
71-
public static int testTwoCastToDifferentType2() {
72-
// The types before and after the two casts are not the same, so the cast cannot be eliminated.
53+
@IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0",
54+
IRNode.VECTOR_MASK_CAST, "= 0" },
55+
applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" },
56+
applyIf = { "MaxVectorSize", ">= 16" })
57+
public static int testOneCastToSameType() {
58+
// The types before and after the cast sequence are the same,
59+
// so the casts will be eliminated.
7360
VectorMask<Integer> mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0);
74-
VectorMask<Double> mDouble256 = mInt128.cast(DoubleVector.SPECIES_256);
75-
VectorMask<Short> mShort64 = mDouble256.cast(ShortVector.SPECIES_64);
76-
return mShort64.trueCount();
61+
mInt128 = mInt128.cast(IntVector.SPECIES_128);
62+
// Insert a not() to prevent the casts being optimized by the optimization:
63+
// (VectorStoreMask (VectorMaskCast* (VectorLoadMask x))) => x
64+
return mInt128.not().trueCount();
7765
}
7866

79-
@Run(test = "testTwoCastToDifferentType2")
80-
public static void testTwoCastToDifferentType2_runner() {
81-
int count = testTwoCastToDifferentType2();
67+
@Run(test = "testOneCastToSameType")
68+
public static void testOneCastToSameType_runner() {
69+
int count = testOneCastToSameType();
8270
VectorMask<Integer> mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0);
83-
Asserts.assertEquals(count, mInt128.trueCount());
71+
Asserts.assertEquals(count, mInt128.not().trueCount());
8472
}
8573

8674
@Test
87-
@IR(counts = { IRNode.VECTOR_MASK_CAST, "= 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"})
75+
@IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0",
76+
IRNode.VECTOR_MASK_CAST, "= 0" },
77+
applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" },
78+
applyIf = { "MaxVectorSize", ">= 16" })
8879
public static int testTwoCastToSameType() {
89-
// The types before and after the two casts are the same, so the cast will be eliminated.
80+
// The types before and after the cast sequence are the same,
81+
// so the casts will be eliminated.
9082
VectorMask<Integer> mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0);
9183
VectorMask<Float> mFloat128 = mInt128.cast(FloatVector.SPECIES_128);
9284
VectorMask<Integer> mInt128_2 = mFloat128.cast(IntVector.SPECIES_128);
93-
return mInt128_2.trueCount();
85+
return mInt128_2.not().trueCount();
9486
}
9587

9688
@Run(test = "testTwoCastToSameType")
9789
public static void testTwoCastToSameType_runner() {
9890
int count = testTwoCastToSameType();
9991
VectorMask<Integer> mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0);
100-
Asserts.assertEquals(count, mInt128.trueCount());
92+
Asserts.assertEquals(count, mInt128.not().trueCount());
10193
}
10294

10395
@Test
104-
@IR(counts = { IRNode.VECTOR_MASK_CAST, "= 1" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"})
96+
@IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0",
97+
IRNode.VECTOR_MASK_CAST, "= 1" },
98+
applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" },
99+
applyIf = { "MaxVectorSize", ">= 16" })
105100
public static int testOneCastToDifferentType() {
106-
// The types before and after the only cast are different, the cast will not be eliminated.
107-
VectorMask<Float> mFloat128 = VectorMask.fromArray(FloatVector.SPECIES_128, mr, 0).not();
108-
VectorMask<Integer> mInt128 = mFloat128.cast(IntVector.SPECIES_128);
109-
return mInt128.trueCount();
101+
// The types before and after the cast sequence are different,
102+
// so the casts will not be eliminated.
103+
VectorMask<Integer> mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0);
104+
VectorMask<Short> mShort64 = mInt128.cast(ShortVector.SPECIES_64);
105+
return mShort64.not().trueCount();
110106
}
111107

112108
@Run(test = "testOneCastToDifferentType")
113109
public static void testOneCastToDifferentType_runner() {
114110
int count = testOneCastToDifferentType();
115-
VectorMask<Float> mInt128 = VectorMask.fromArray(FloatVector.SPECIES_128, mr, 0).not();
116-
Asserts.assertEquals(count, mInt128.trueCount());
111+
VectorMask<Integer> mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0);
112+
Asserts.assertEquals(count, mInt128.not().trueCount());
113+
}
114+
115+
@Test
116+
@IR(counts = { IRNode.LOAD_VECTOR_Z, IRNode.VECTOR_SIZE_4, "> 0",
117+
IRNode.VECTOR_MASK_CAST, "= 2" },
118+
applyIfCPUFeatureOr = { "asimd", "true", "avx2", "true", "rvv", "true" },
119+
applyIf = { "MaxVectorSize", ">= 16" })
120+
public static int testTwoCastToDifferentType() {
121+
// The types before and after the cast sequence are different, so the
122+
// casts are not eliminated. We should probably be able to eliminate
123+
// the intermediate cast, so that we only need a cast from short to int.
124+
VectorMask<Short> mShort64 = VectorMask.fromArray(ShortVector.SPECIES_64, mr, 0);
125+
VectorMask<Float> mFloat128 = mShort64.cast(FloatVector.SPECIES_128);
126+
VectorMask<Integer> mInt128 = mFloat128.cast(IntVector.SPECIES_128);
127+
return mInt128.not().trueCount();
128+
}
129+
130+
@Run(test = "testTwoCastToDifferentType")
131+
public static void testTwoCastToDifferentType_runner() {
132+
int count = testTwoCastToDifferentType();
133+
VectorMask<Short> mShort64 = VectorMask.fromArray(ShortVector.SPECIES_64, mr, 0);
134+
Asserts.assertEquals(count, mShort64.not().trueCount());
117135
}
118136

119137
public static void main(String[] args) {

0 commit comments

Comments
 (0)