diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index e67352d85bdcc..febff7b740bf0 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -237,58 +237,167 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg jlong hi = bt == T_INT ? max_jint : max_jlong; jlong lo = bt == T_INT ? min_jint : min_jlong; + assert(bt == T_INT || bt == T_LONG, ""); - if(mask_type->is_con() && mask_type->get_con_as_long(bt) != -1L) { + // Rule 1: Bit compression selects the source bits corresponding to true mask bits, + // packs them and places them contiguously at destination bit positions + // starting from least significant bit, remaining higher order bits are set + // to zero. + + // Rule 2: Bit expansion is a reverse process, which sequentially reads source bits + // starting from LSB and places them at bit positions in result value where + // corresponding mask bits are 1. Thus, bit expansion for non-negative mask + // value will always generate a +ve value, this is because sign bit of result + // will never be set to 1 as corresponding mask bit is always 0. + + // Case A) Constant mask + if (mask_type->is_con()) { jlong maskcon = mask_type->get_con_as_long(bt); - int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); if (opc == Op_CompressBits) { - // Bit compression selects the source bits corresponding to true mask bits - // and lays them out contiguously at destination bit positions starting from - // LSB, remaining higher order bits are set to zero. - // Thus, it will always generate a +ve value i.e. sign bit set to 0 if - // any bit of constant mask value is zero. - lo = 0L; - hi = (1UL << bitcount) - 1; + // Case A.1 bit compression:- + // For an outlier mask value of -1 upper bound of the result equals + // maximum integral value, for any other mask value its computed using + // following formula + // Result.Hi = 1 << popcount(mask_bits) - 1 + // + // For mask values other than -1, lower bound of the result is estimated + // as zero, by assuming at least one mask bit is zero and corresponding source + // bit will be masked, hence result of bit compression will always be + // non-negative value. For outlier mask value of -1, assume all source bits + // apart from most significant bit were set to 0, thereby resulting in + // a minimum integral value. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0xEFFFFFFF (constant mask) + // result.hi = 0x7FFFFFFF + // result.lo = 0 + if (maskcon != -1L) { + int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); + hi = (1UL << bitcount) - 1; + lo = 0L; + } else { + // preserve originally assigned hi (MAX_INT/LONG) and lo (MIN_INT/LONG) values + // for unknown source bits. + assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); + assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); + } } else { + // Case A.2 bit expansion:- assert(opc == Op_ExpandBits, ""); - // Expansion sequentially reads source bits starting from LSB - // and places them over destination at bit positions corresponding - // set mask bit. Thus bit expansion for non-negative mask value - // will always generate a +ve value. - hi = maskcon >= 0L ? maskcon : maskcon ^ lo; - lo = maskcon >= 0L ? 0L : lo; + if (maskcon >= 0L) { + // Case A.2.1 constant mask >= 0 + // Result.Hi = mask, optimistically assuming all source bits + // read starting from least significant bit positions are 1. + // Result.Lo = 0, because at least one bit in mask is zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0x7FFFFFFF (constant mask >= 0) + // result.hi = 0x7FFFFFFF + // result.lo = 0 + hi = maskcon; + lo = 0L; + } else { + // Case A.2.2) mask < 0 + // For constant mask strictly less than zero, the maximum result value will be + // the same as the mask value with its sign bit flipped, assuming all source bits + // except the MSB bit are set(one). + // + // To compute minimum result value we assume all but last read source bit as zero, + // this is because sign bit of result will always be set to 1 while other bit + // corresponding to set mask bit should be zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0xEFFFFFFF (constant mask) + // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF + // result.lo = 0x80000000 + // + hi = maskcon ^ lo; + // lo still retains MIN_INT/LONG. + assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); + } } } + // Case B) Non-constant mask. if (!mask_type->is_con()) { - int mask_max_bw; - int max_bw = bt == T_INT ? 32 : 64; - // Case 1) Mask value range includes -1. - if ((mask_type->lo_as_long() < 0L && mask_type->hi_as_long() >= -1L)) { - mask_max_bw = max_bw; - // Case 2) Mask value range is less than -1. - } else if (mask_type->hi_as_long() < -1L) { - mask_max_bw = max_bw - 1; - } else { - // Case 3) Mask value range only includes +ve values. - assert(mask_type->lo_as_long() >= 0, ""); - jlong clz = count_leading_zeros(mask_type->hi_as_long()); - clz = bt == T_INT ? clz - 32 : clz; - mask_max_bw = max_bw - clz; - } if ( opc == Op_CompressBits) { - lo = mask_max_bw == max_bw ? lo : 0L; - // Compress operation is inherently an unsigned operation and - // result value range is primarily dependent on true count - // of participating mask value. - hi = mask_max_bw < max_bw ? (1L << mask_max_bw) - 1 : src_type->hi_as_long(); + int result_bit_width; + int mask_bit_width = bt == T_INT ? 32 : 64; + if ((mask_type->lo_as_long() < 0L && mask_type->hi_as_long() >= -1L)) { + // Case B.1 The mask value range includes -1, hence we may use all bits, + // the result has the whole value range. + result_bit_width = mask_bit_width; + } else if (mask_type->hi_as_long() < -1L) { + // Case B.2 Mask value range is strictly less than -1, this indicates presence of at least + // one unset(zero) bit in mask value, thus as per Rule 1, bit compression will always + // result in a non-negative value. This guarantees that MSB bit of result value will + // always be set to zero. + result_bit_width = mask_bit_width - 1; + } else { + assert(mask_type->lo_as_long() >= 0, ""); + // Case B.3 Mask value range only includes non-negative values. Since all integral + // types honours an invariant that TypeInteger._lo <= TypeInteger._hi, thus computing + // leading zero bits of upper bound of mask value will allow us to ascertain + // optimistic upper bound of result i.e. all the bits other than leading zero bits + // can be assumed holding 1 value. + jlong clz = count_leading_zeros(mask_type->hi_as_long()); + // Here, result of clz is w.r.t to long argument, hence for integer argument + // we explicitly subtract 32 from the result. + clz = bt == T_INT ? clz - 32 : clz; + result_bit_width = mask_bit_width - clz; + } + // If the number of bits required to for the mask value range is less than the + // full bit width of the integral type, then the MSB bit is guaranteed to be zero, + // thus the compression result will never be a -ve value and we can safely set the + // lower bound of the bit compression to zero. + lo = result_bit_width == mask_bit_width ? lo : 0L; + + assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); + assert(lo == (bt == T_INT ? min_jint : min_jlong) || lo == 0, ""); + + if (src_type->lo_as_long() >= 0) { + // Lemma 1: For strictly non-negative src, the result of the compression will never be + // greater than src. + // Proof: Since src is a non-negative value, its most significant bit is always 0. + // Thus even if the corresponding MSB of the mask is one, the result will be a +ve + // value. There are three possible cases + // a. All the mask bits corresponding to set source bits are unset(zero). + // b. All the mask bits corresponding to set source bits are set(one) + // c. Some mask bits corresponding to set source bits are set(one) while others are unset(zero) + // + // Case a. results into an allzero result, while Case b. gives us the upper bound which is equals source + // value, while for Case c. the result will lie within [0, src] + // + hi = src_type->hi_as_long(); + lo = 0L; + } + + if (result_bit_width < mask_bit_width) { + // Rule 3: + // We can further constrain the upper bound of bit compression if the number of bits + // which can be set(one) is less than the maximum number of bits of integral type. + hi = MIN2((jlong)((1UL << result_bit_width) - 1L), hi); + } } else { assert(opc == Op_ExpandBits, ""); jlong max_mask = mask_type->hi_as_long(); + jlong min_mask = mask_type->lo_as_long(); // Since mask here a range and not a constant value, hence being // conservative in determining the value range of result. - lo = mask_type->lo_as_long() >= 0L ? 0L : lo; - hi = mask_type->lo_as_long() >= 0L ? max_mask : hi; + if (min_mask >= 0L) { + // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, + // if the lower bound of non-constant mask is a non-negative value then result can never + // be greater than the mask. + // Proof: Since lower bound of the mask is a non-negative value, hence most significant + // bit of its entire value must be unset(zero). If all the lower order 'n' source bits + // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals + // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' + // source bits are unset(zero) there by resuling into a zero value. + hi = max_mask; + lo = 0; + } else { + // preserve the lo and hi bounds estimated till now. + } } } @@ -329,6 +438,11 @@ const Type* CompressBitsNode::Value(PhaseGVN* phase) const { static_cast(TypeLong::make(res)); } + // Result is zero if src is zero irrespective of mask value. + if (src_type == TypeInteger::zero(bt)) { + return TypeInteger::zero(bt); + } + return bitshuffle_value(src_type, mask_type, Op_CompressBits, bt); } @@ -365,5 +479,10 @@ const Type* ExpandBitsNode::Value(PhaseGVN* phase) const { static_cast(TypeLong::make(res)); } + // Result is zero if src is zero irrespective of mask value. + if (src_type == TypeInteger::zero(bt)) { + return TypeInteger::zero(bt); + } + return bitshuffle_value(src_type, mask_type, Op_ExpandBits, bt); } diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java b/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java new file mode 100644 index 0000000000000..98f26f120b191 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestBitCompressValueTransform.java @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2025, 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. + */ + +/* + * @test + * @bug 8350896 + * @library /test/lib / + * @summary C2: wrong result: Integer/Long.compress gets wrong type from CompressBitsNode::Value. + * @run driver compiler.c2.gvn.TestBitCompressValueTransform + */ +package compiler.c2.gvn; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import compiler.lib.generators.*; + +public class TestBitCompressValueTransform { + + public static final int field_I = 0x400_0000; + public static final long field_L = 0x400_0000_0000_0000L; + public static final int gold_I = Integer.valueOf(Integer.compress(0x8000_0000, field_I)); + public static final long gold_L = Long.valueOf(Long.compress(0x8000_0000_0000_0000L, field_L)); + + public static RestrictableGenerator GEN_I = Generators.G.ints(); + public static RestrictableGenerator GEN_L = Generators.G.longs(); + + public final int LIMIT_I1 = GEN_I.next(); + public final int LIMIT_I2 = GEN_I.next(); + public final int LIMIT_I3 = GEN_I.next(); + public final int LIMIT_I4 = GEN_I.next(); + public final int LIMIT_I5 = GEN_I.next(); + public final int LIMIT_I6 = GEN_I.next(); + public final int LIMIT_I7 = GEN_I.next(); + public final int LIMIT_I8 = GEN_I.next(); + + public final long LIMIT_L1 = GEN_L.next(); + public final long LIMIT_L2 = GEN_L.next(); + public final long LIMIT_L3 = GEN_L.next(); + public final long LIMIT_L4 = GEN_L.next(); + public final long LIMIT_L5 = GEN_L.next(); + public final long LIMIT_L6 = GEN_L.next(); + public final long LIMIT_L7 = GEN_L.next(); + public final long LIMIT_L8 = GEN_L.next(); + + public final int BOUND_LO_I = GEN_I.next(); + public final int BOUND_HI_I = GEN_I.next(); + + public final long BOUND_LO_L = GEN_L.next(); + public final long BOUND_HI_L = GEN_L.next(); + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public long test1(long value) { + return Long.compress(0x8000_0000_0000_0000L, value); + } + + @Run(test = "test1") + public void run1(RunInfo info) { + long res = 0; + for (int i = 0; i < 10000; i++) { + res |= test1(field_L); + } + Asserts.assertEQ(res, gold_L); + } + + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public int test2(int value) { + return Integer.compress(0x8000_0000, value); + } + + @Run(test = "test2") + public void run2(RunInfo info) { + int res = 0; + for (int i = 0; i < 10000; i++) { + res |= test2(field_I); + } + Asserts.assertEQ(res, gold_I); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " 0 "} , failOn = { IRNode.UNSTABLE_IF_TRAP }, applyIfCPUFeature = { "bmi2", "true" }) + public int test3(int value) { + int filter_bits = value & 0xF; + int compress_bits = Integer.compress(15, filter_bits); + if (compress_bits > 15) { + value = -1; + } + return value; + } + + @Run(test = "test3") + public void run3(RunInfo info) { + int res = 0; + for (int i = 1; i < 10000; i++) { + res |= test3(i); + } + Asserts.assertLTE(0, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " 0 "} , failOn = { IRNode.UNSTABLE_IF_TRAP }, applyIfCPUFeature = { "bmi2", "true" }) + public long test4(long value) { + long filter_bits = value & 0xFL; + long compress_bits = Long.compress(15L, filter_bits); + if (compress_bits > 15L) { + value = -1; + } + return value; + } + + @Run(test = "test4") + public void run4(RunInfo info) { + long res = 0; + for (long i = 1; i < 10000; i++) { + res |= test4(i); + } + Asserts.assertLTE(0L, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public long test5(long value) { + // Since value range includes -1 hence with mask + // and value as -1 all the result bits will be set. + long mask = Long.min(10000L, Long.max(-10000L, value)); + return Long.compress(value, mask); + } + + @Run(test = "test5") + public void run5(RunInfo info) { + long res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test5((long)i); + } + Asserts.assertEQ(-1L, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public long test6(long value) { + // For mask within a strictly -ve value range less than -1, + // result of compression will always be a +ve value. + long mask = Long.min(-2L, Long.max(-10000L, value)); + return Long.compress(value, mask); + } + + @Run(test = "test6") + public void run6(RunInfo info) { + long res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test6((long)i); + } + Asserts.assertLTE(0L, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public long test7(long value) { + // For mask within a strictly +ve value range, + // result of compression will always be a +ve value with + // upper bound capped at max mask value. + long mask = Long.min(10000L, Long.max(0L, value)); + return Long.compress(value, mask); + } + + @Run(test = "test7") + public void run7(RunInfo info) { + long res = Long.MIN_VALUE; + for (int i = -10000; i < 10000; i++) { + res = Long.max(test7((long)i), res); + } + Asserts.assertGTE(10000L, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public int test8(int value) { + // Since value range includes -1 hence with mask + // and value as -1 all the result bits will be set. + int mask = Integer.min(10000, Integer.max(-10000, value)); + return Integer.compress(value, mask); + } + + @Run(test = "test8") + public void run8(RunInfo info) { + int res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test8(i); + } + Asserts.assertEQ(-1, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public int test9(int value) { + // For mask within a strictly -ve value range less than -1, + // result of compression will always be a +ve value. + int mask = Integer.min(-2, Integer.max(-10000, value)); + return Integer.compress(value, mask); + } + + @Run(test = "test9") + public void run9(RunInfo info) { + int res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test9(i); + } + Asserts.assertLTE(0, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = { "bmi2", "true" }) + public int test10(int value) { + // For mask within a strictly +ve value range, + // result of compression will always be a +ve value with + // upper bound capped at max mask value. + int mask = Integer.min(10000, Integer.max(0, value)); + return Integer.compress(value, mask); + } + + @Run(test = "test10") + public void run10(RunInfo info) { + int res = Integer.MIN_VALUE; + for (int i = -10000; i < 10000; i++) { + res = Integer.max(test10(i), res); + } + Asserts.assertGTE(10000, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " 0 " }) + public int test11(int value) { + // For constant zero input, compress is folded to zero + int mask = Integer.min(10000, Integer.max(0, value)); + return Integer.compress(0, mask); + } + + @Run(test = "test11") + public void run11(RunInfo info) { + int res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test11(i); + } + Asserts.assertEQ(0, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " 0 " }) + public long test12(long value) { + // For constant zero input, compress is folded to zero + long mask = Long.min(10000L, Long.max(0L, value)); + return Long.compress(0L, mask); + } + + @Run(test = "test12") + public void run12(RunInfo info) { + long res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test12(i); + } + Asserts.assertEQ(0L, res); + } + + @Test + @IR (counts = { IRNode.EXPAND_BITS, " 0 " }) + public int test13(int value) { + // For constant zero input, expand is folded to zero + int mask = Integer.min(10000, Integer.max(0, value)); + return Integer.expand(0, mask); + } + + @Run(test = "test13") + public void run13(RunInfo info) { + int res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test13(i); + } + Asserts.assertEQ(0, res); + } + + @Test + @IR (counts = { IRNode.EXPAND_BITS, " 0 " }) + public long test14(long value) { + // For constant zero input, compress is folded to zero + long mask = Long.min(10000L, Long.max(0L, value)); + return Long.expand(0L, mask); + } + + @Run(test = "test14") + public void run14(RunInfo info) { + long res = 0; + for (int i = -10000; i < 10000; i++) { + res |= test14(i); + } + Asserts.assertEQ(0L, res); + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + public int test15(int src, int mask) { + // src_type = [min_int + 1, -1] + src = Math.max(Integer.MIN_VALUE + 1, Math.min(src, -1)); + return Integer.compress(src, mask); + } + + @Run (test = "test15") + public void run15(RunInfo info) { + int res = 0; + for (int i = 0; i < 10000; i++) { + res |= test15(0, 0); + } + Asserts.assertEQ(0, res); + } + + @DontCompile + public int test16_interpreted(int src, int mask) { + src = Math.max(BOUND_LO_I, Math.min(src, BOUND_HI_I)); + int res = Integer.compress(src, mask); + + if (res > LIMIT_I1) { + res += 1; + } + if (res > LIMIT_I2) { + res += 2; + } + if (res > LIMIT_I3) { + res += 4; + } + if (res > LIMIT_I4) { + res += 8; + } + if (res > LIMIT_I5) { + res += 16; + } + if (res > LIMIT_I6) { + res += 32; + } + if (res > LIMIT_I7) { + res += 64; + } + if (res > LIMIT_I8) { + res += 128; + } + return res; + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + public int test16(int src, int mask) { + src = Math.max(BOUND_LO_I, Math.min(src, BOUND_HI_I)); + int res = Integer.compress(src, mask); + + // Check the result with some random value ranges, if any of the + // following conditions incorrectly constant folds the result will + // not comply with the interpreter. + + if (res > LIMIT_I1) { + res += 1; + } + if (res > LIMIT_I2) { + res += 2; + } + if (res > LIMIT_I3) { + res += 4; + } + if (res > LIMIT_I4) { + res += 8; + } + if (res > LIMIT_I5) { + res += 16; + } + if (res > LIMIT_I6) { + res += 32; + } + if (res > LIMIT_I7) { + res += 64; + } + if (res > LIMIT_I8) { + res += 128; + } + return res; + } + + @Run (test = "test16") + public void run16(RunInfo info) { + int actual = 0; + int expected = 0; + + for (int i = 0; i < 10000; i++) { + int arg1 = GEN_I.next(); + int arg2 = GEN_I.next(); + + actual += test16(arg1, arg2); + expected += test16_interpreted(arg1, arg2); + } + Asserts.assertEQ(actual, expected); + } + + @DontCompile + public int test17_interpreted(int src, int mask) { + src = Math.max(BOUND_LO_I, Math.min(src, BOUND_HI_I)); + int res = Integer.expand(src, mask); + + if (res > LIMIT_I1) { + res += 1; + } + if (res > LIMIT_I2) { + res += 2; + } + if (res > LIMIT_I3) { + res += 4; + } + if (res > LIMIT_I4) { + res += 8; + } + if (res > LIMIT_I5) { + res += 16; + } + if (res > LIMIT_I6) { + res += 32; + } + if (res > LIMIT_I7) { + res += 64; + } + if (res > LIMIT_I8) { + res += 128; + } + return res; + } + + @Test + @IR (counts = { IRNode.EXPAND_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + public int test17(int src, int mask) { + src = Math.max(BOUND_LO_I, Math.min(src, BOUND_HI_I)); + int res = Integer.expand(src, mask); + + // Check the result with some random value ranges, if any of the + // following conditions incorrectly constant folds the result will + // not comply with the interpreter. + + if (res > LIMIT_I1) { + res += 1; + } + if (res > LIMIT_I2) { + res += 2; + } + if (res > LIMIT_I3) { + res += 4; + } + if (res > LIMIT_I4) { + res += 8; + } + if (res > LIMIT_I5) { + res += 16; + } + if (res > LIMIT_I6) { + res += 32; + } + if (res > LIMIT_I7) { + res += 64; + } + if (res > LIMIT_I8) { + res += 128; + } + return res; + } + + @Run (test = "test17") + public void run17(RunInfo info) { + int actual = 0; + int expected = 0; + + for (int i = 0; i < 10000; i++) { + int arg1 = GEN_I.next(); + int arg2 = GEN_I.next(); + + actual += test16(arg1, arg2); + expected += test16_interpreted(arg1, arg2); + } + Asserts.assertEQ(actual, expected); + } + + @DontCompile + public long test18_interpreted(long src, long mask) { + src = Math.max(BOUND_LO_L, Math.min(src, BOUND_HI_L)); + long res = Long.compress(src, mask); + + if (res > LIMIT_L1) { + res += 1; + } + if (res > LIMIT_L2) { + res += 2; + } + if (res > LIMIT_L3) { + res += 4; + } + if (res > LIMIT_L4) { + res += 8; + } + if (res > LIMIT_L5) { + res += 16; + } + if (res > LIMIT_L6) { + res += 32; + } + if (res > LIMIT_L7) { + res += 64; + } + if (res > LIMIT_L8) { + res += 128; + } + return res; + } + + @Test + @IR (counts = { IRNode.COMPRESS_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + public long test18(long src, long mask) { + src = Math.max(BOUND_LO_L, Math.min(src, BOUND_HI_L)); + long res = Long.compress(src, mask); + + // Check the result with some random value ranges, if any of the + // following conditions incorrectly constant folds the result will + // not comply with the interpreter. + + if (res > LIMIT_L1) { + res += 1; + } + if (res > LIMIT_L2) { + res += 2; + } + if (res > LIMIT_L3) { + res += 4; + } + if (res > LIMIT_L4) { + res += 8; + } + if (res > LIMIT_L5) { + res += 16; + } + if (res > LIMIT_L6) { + res += 32; + } + if (res > LIMIT_L7) { + res += 64; + } + if (res > LIMIT_L8) { + res += 128; + } + return res; + } + + @Run (test = "test18") + public void run18(RunInfo info) { + long actual = 0; + long expected = 0; + + for (int i = 0; i < 10000; i++) { + long arg1 = GEN_L.next(); + long arg2 = GEN_L.next(); + + actual += test18(arg1, arg2); + expected += test18_interpreted(arg1, arg2); + } + Asserts.assertEQ(actual, expected); + } + + @DontCompile + public long test19_interpreted(long src, long mask) { + src = Math.max(BOUND_LO_L, Math.min(src, BOUND_HI_L)); + long res = Long.expand(src, mask); + + if (res > LIMIT_L1) { + res += 1; + } + if (res > LIMIT_L2) { + res += 2; + } + if (res > LIMIT_L3) { + res += 4; + } + if (res > LIMIT_L4) { + res += 8; + } + if (res > LIMIT_L5) { + res += 16; + } + if (res > LIMIT_L6) { + res += 32; + } + if (res > LIMIT_L7) { + res += 64; + } + if (res > LIMIT_L8) { + res += 128; + } + return res; + } + + @Test + @IR (counts = { IRNode.EXPAND_BITS, " >0 " }, applyIfCPUFeature = {"bmi2" , "true"}) + public long test19(long src, long mask) { + src = Math.max(BOUND_LO_L, Math.min(src, BOUND_HI_L)); + long res = Long.expand(src, mask); + + // Check the result with some random value ranges, if any of the + // following conditions incorrectly constant folds the result will + // not comply with the interpreter. + + if (res > LIMIT_L1) { + res += 1; + } + if (res > LIMIT_L2) { + res += 2; + } + if (res > LIMIT_L3) { + res += 4; + } + if (res > LIMIT_L4) { + res += 8; + } + if (res > LIMIT_L5) { + res += 16; + } + if (res > LIMIT_L6) { + res += 32; + } + if (res > LIMIT_L7) { + res += 64; + } + if (res > LIMIT_L8) { + res += 128; + } + return res; + } + + @Run (test = "test19") + public void run19(RunInfo info) { + long actual = 0; + long expected = 0; + + for (int i = 0; i < 10000; i++) { + long arg1 = GEN_L.next(); + long arg2 = GEN_L.next(); + + actual += test19(arg1, arg2); + expected += test19_interpreted(arg1, arg2); + } + Asserts.assertEQ(actual, expected); + } + + public static void main(String[] args) { + TestFramework.run(TestBitCompressValueTransform.class); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java index 7d229cae15201..6468a382d4147 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java @@ -104,6 +104,7 @@ public class IREncodingPrinter { "avx512f", "avx512_fp16", "avx512_vnni", + "bmi2", // AArch64 "sha3", "asimd",