jdk/test/hotspot/jtreg/compiler/vectorapi/TestVectorMulLongToSignedUnsignedInt.java
2026-06-15 12:01:09 +00:00

353 lines
15 KiB
Java

/*
* Copyright (c) 2026, 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.
*/
package compiler.vectorapi;
import jdk.incubator.vector.*;
import static jdk.incubator.vector.VectorOperators.AND;
import static jdk.incubator.vector.VectorOperators.MUL;
import static jdk.incubator.vector.VectorOperators.LSHR;
import static jdk.incubator.vector.VectorOperators.ASHR;
import compiler.lib.generators.Generators;
import compiler.lib.ir_framework.*;
import compiler.lib.verify.*;
/*
* @test
* @bug 8384963
* @key randomness
* @summary C2: Incorrect uint constant match mishandles negative values in vectors
* @modules jdk.incubator.vector
* @library /test/lib /
* @run driver compiler.vectorapi.TestVectorMulLongToSignedUnsignedInt
*/
public class TestVectorMulLongToSignedUnsignedInt {
static final VectorSpecies<Long> SPECIES = LongVector.SPECIES_PREFERRED;
static final int SIZE = SPECIES.length();
private static final Generators RD = Generators.G;
static final long[] src1 = new long[SIZE];
static final long[] src2 = new long[SIZE];
static long[] res = new long[SIZE];
static final boolean[] mask_arr = new boolean[SIZE];
static final VectorMask<Long> MASK;
// Random compile-time-constant masks.
static final long RAND_MASK1 = RD.longs().next();
static final long RAND_MASK2 = RD.longs().next();
// Random compile-time-constant shift count in [0, 63]. A shift >= 32 clears
// the upper doubleword (fits uint); a smaller shift may not.
static final int RAND_SHIFT1 = RD.ints().next() & 0x3F;
// Random input arrays for the random-mask correctness cases.
static final long[] rsrc1 = new long[SIZE];
static final long[] rsrc2 = new long[SIZE];
static {
for (int i = 0; i < SIZE; i++) {
src1[i] = 0x1_0000_0001L;
src2[i] = 0x2_0000_0002L;
mask_arr[i] = (i % 2) == 0;
}
MASK = VectorMask.fromArray(SPECIES, mask_arr, 0);
RD.fill(RD.longs(), rsrc1);
RD.fill(RD.longs(), rsrc2);
}
public static void main(String[] args) {
TestFramework testFramework = new TestFramework();
testFramework.setDefaultWarmup(10000)
.addFlags("--add-modules=jdk.incubator.vector")
.start();
}
// Case 1: Negative mask (-2L = 0xFFFF_FFFF_FFFF_FFFE).
@Test
@IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testNegativeMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, -2L).lanewise(MUL, v2.lanewise(AND, -2L)).intoArray(res, 0);
}
@Run(test = "testNegativeMask")
public void runNegativeMask() {
testNegativeMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] & -2L) * (src2[i] & -2L);
}
Verify.checkEQ(res, expected);
}
// Case 3: Mask = 0x1_0000_0000L (bit 32 set, exceeds uint range).
@Test
@IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testBit32SetMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, 0x1_0000_0000L).lanewise(MUL, v2.lanewise(AND, 0x1_0000_0000L)).intoArray(res, 0);
}
@Run(test = "testBit32SetMask")
public void runBit32SetMask() {
testBit32SetMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] & 0x1_0000_0000L) * (src2[i] & 0x1_0000_0000L);
}
Verify.checkEQ(res, expected);
}
// Case 4: Mask = Long.MIN_VALUE (0x8000_0000_0000_0000).
@Test
@IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testMinValueMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, Long.MIN_VALUE).lanewise(MUL, v2.lanewise(AND, Long.MIN_VALUE)).intoArray(res, 0);
}
@Run(test = "testMinValueMask")
public void runMinValueMask() {
testMinValueMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] & Long.MIN_VALUE) * (src2[i] & Long.MIN_VALUE);
}
Verify.checkEQ(res, expected);
}
// Case 5: Mask = 0xFFFF_FFFFL (exactly uint max, boundary valid case).
@Test
@IR(counts = {IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testUintMaxMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, 0xFFFF_FFFFL).lanewise(MUL, v2.lanewise(AND, 0xFFFF_FFFFL)).intoArray(res, 0);
}
@Run(test = "testUintMaxMask")
public void runUintMaxMask() {
testUintMaxMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] & 0xFFFF_FFFFL) * (src2[i] & 0xFFFF_FFFFL);
}
Verify.checkEQ(res, expected);
}
// Case 6: Small mask (0xFFFFL), clearly fits in uint.
@Test
@IR(counts = {IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testSmallMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, 0xFFFFL).lanewise(MUL, v2.lanewise(AND, 0xFFFFL)).intoArray(res, 0);
}
@Run(test = "testSmallMask")
public void runSmallMask() {
testSmallMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] & 0xFFFFL) * (src2[i] & 0xFFFFL);
}
Verify.checkEQ(res, expected);
}
// Case 7: URShift by 32 clears upper doubleword.
@Test
@IR(counts = {IRNode.MUL_VL, " >0 ", IRNode.URSHIFT_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(counts = {IRNode.X86_VMULUDQ_REG, " >0 "}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testURShift32() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(LSHR, 32).lanewise(MUL, v2.lanewise(LSHR, 32)).intoArray(res, 0);
}
@Run(test = "testURShift32")
public void runURShift32() {
testURShift32();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] >>> 32) * (src2[i] >>> 32);
}
Verify.checkEQ(res, expected);
}
// Case 8: Asymmetric — one input valid uint mask, other negative mask.
@Test
@IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testAsymmetricMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, 0xFFFF_FFFFL).lanewise(MUL, v2.lanewise(AND, -2L)).intoArray(res, 0);
}
@Run(test = "testAsymmetricMask")
public void runAsymmetricMask() {
testAsymmetricMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] & 0xFFFF_FFFFL) * (src2[i] & -2L);
}
Verify.checkEQ(res, expected);
}
// Case 9: Mixed — one input URShift (valid), other negative mask (invalid).
// Note: -2L is used (not -1L) since AND with -1L is identity and gets folded.
@Test
@IR(counts = {IRNode.URSHIFT_VL, " >0 ", IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx", "true"})
public static void testMixedURShiftAndNegMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(LSHR, 32).lanewise(MUL, v2.lanewise(AND, -2L)).intoArray(res, 0);
}
@Run(test = "testMixedURShiftAndNegMask")
public void runMixedURShiftAndNegMask() {
testMixedURShiftAndNegMask();
long[] expected = new long[SPECIES.length()];
for (int i = 0; i < SPECIES.length(); i++) {
expected[i] = (src1[i] >>> 32) * (src2[i] & -2L);
}
Verify.checkEQ(res, expected);
}
// Case 10: Predicated AndV (uint path). Inactive lanes preserves destination with non-zero upper 32 bits.
@Test
@IR(counts = {IRNode.AND_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx512f", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"})
public static void testPredicatedAndMask() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(AND, 0xFFFF_FFFFL, MASK).lanewise(MUL, v2.lanewise(AND, 0xFFFF_FFFFL, MASK)).intoArray(res, 0);
}
@Run(test = "testPredicatedAndMask")
public void runPredicatedAndMask() {
testPredicatedAndMask();
long[] expected = new long[SIZE];
for (int i = 0; i < SIZE; i++) {
long a = mask_arr[i] ? (src1[i] & 0xFFFF_FFFFL) : src1[i];
long b = mask_arr[i] ? (src2[i] & 0xFFFF_FFFFL) : src2[i];
expected[i] = a * b;
}
Verify.checkEQ(res, expected);
}
// Case 11: Predicated URShiftVL by 32 (uint path). Inactive lanes preserves destination with non-zero upper 32 bits.
@Test
@IR(counts = {IRNode.URSHIFT_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx512f", "true"})
@IR(failOn = {IRNode.X86_VMULUDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"})
public static void testPredicatedURShift32() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(LSHR, 32, MASK).lanewise(MUL, v2.lanewise(LSHR, 32, MASK)).intoArray(res, 0);
}
@Run(test = "testPredicatedURShift32")
public void runPredicatedURShift32() {
testPredicatedURShift32();
long[] expected = new long[SIZE];
for (int i = 0; i < SIZE; i++) {
long a = mask_arr[i] ? (src1[i] >>> 32) : src1[i];
long b = mask_arr[i] ? (src2[i] >>> 32) : src2[i];
expected[i] = a * b;
}
Verify.checkEQ(res, expected);
}
// Case 12: Predicated RShiftVL (arithmetic) by 32.
@Test
@IR(counts = {IRNode.RSHIFT_VL, " >0 ", IRNode.MUL_VL, " >0 "}, applyIfCPUFeature = {"avx512f", "true"})
@IR(failOn = {IRNode.X86_VMULDQ_REG}, phase = CompilePhase.MATCHING, applyIfCPUFeature = {"avx512f", "true"})
public static void testPredicatedRShift32() {
LongVector v1 = LongVector.fromArray(SPECIES, src1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, src2, 0);
v1.lanewise(ASHR, 32, MASK).lanewise(MUL, v2.lanewise(ASHR, 32, MASK)).intoArray(res, 0);
}
@Run(test = "testPredicatedRShift32")
public void runPredicatedRShift32() {
testPredicatedRShift32();
long[] expected = new long[SIZE];
for (int i = 0; i < SIZE; i++) {
long a = mask_arr[i] ? (src1[i] >> 32) : src1[i];
long b = mask_arr[i] ? (src2[i] >> 32) : src2[i];
expected[i] = a * b;
}
Verify.checkEQ(res, expected);
}
// Random-constant correctness cases with no IR rules.
// Case 13: AND pattern with random masks on both inputs.
@Test
public static void testRandomAndMasks() {
LongVector v1 = LongVector.fromArray(SPECIES, rsrc1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, rsrc2, 0);
v1.lanewise(AND, RAND_MASK1).lanewise(MUL, v2.lanewise(AND, RAND_MASK2)).intoArray(res, 0);
}
@Run(test = "testRandomAndMasks")
public void runRandomAndMasks() {
testRandomAndMasks();
long[] expected = new long[SIZE];
for (int i = 0; i < SIZE; i++) {
expected[i] = (rsrc1[i] & RAND_MASK1) * (rsrc2[i] & RAND_MASK2);
}
Verify.checkEQ(res, expected);
}
// Case 14: URShiftV pattern with a random shift count on both inputs.
@Test
public static void testRandomURShift() {
LongVector v1 = LongVector.fromArray(SPECIES, rsrc1, 0);
LongVector v2 = LongVector.fromArray(SPECIES, rsrc2, 0);
v1.lanewise(LSHR, RAND_SHIFT1).lanewise(MUL, v2.lanewise(LSHR, RAND_SHIFT1)).intoArray(res, 0);
}
@Run(test = "testRandomURShift")
public void runRandomURShift() {
testRandomURShift();
long[] expected = new long[SIZE];
for (int i = 0; i < SIZE; i++) {
expected[i] = (rsrc1[i] >>> RAND_SHIFT1) * (rsrc2[i] >>> RAND_SHIFT1);
}
Verify.checkEQ(res, expected);
}
}