From 808d8067829885d99e369adccfcd436532eaf4b4 Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Sat, 29 Jun 2024 15:47:29 -0400 Subject: [PATCH 1/4] Improve result of AndNode mul_ring --- src/hotspot/share/opto/mulnode.cpp | 90 ++++++++++--------- .../c2/irTests/AndINodeIdealizationTests.java | 66 +++++++++++++- .../c2/irTests/AndLNodeIdealizationTests.java | 66 +++++++++++++- 3 files changed, 178 insertions(+), 44 deletions(-) diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 1f22c60832388..5a60a4f8c91e1 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -594,6 +594,50 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { return TypeLong::LONG; } +template +static const IntegerType* and_value(const IntegerType* r0, const IntegerType* r1) { + typedef typename IntegerType::NativeType NativeType; + + int widen = MAX2(r0->_widen,r1->_widen); + + // If both are constants, we can calculate a precise result. + if(r0->is_con() && r1->is_con()) { + return IntegerType::make(r0->get_con() & r1->get_con()); + } + + // If both ranges are positive, the result will range from 0 up to the maximum value of the smaller range. + if (r0->_lo >= 0 && r1->_lo >= 0) { + return IntegerType::make(0, MIN2(r0->_hi, r1->_hi), widen); + } + + // If only one range is positive, the result will range from 0 up to that range's maximum value. + if (r0->_lo >= 0) { + return IntegerType::make(0, r0->_hi, widen); + } + + if (r1->_lo >= 0) { + return IntegerType::make(0, r1->_hi, widen); + } + + // At this point, all positive ranges will have already been handled, so the only remaining cases will be negative ranges + // and constants. + + assert(r0->_lo < 0 && r1->_lo < 0, "positive ranges should already be handled!"); + static_assert(std::is_signed::value, "native type of IntegerType must be signed!"); + + // The lower bound of both ranges will contain the common leading 1-bits. In order to count them with count_leading_zeros, + // the bits are inverted. + NativeType sel_val = ~MIN2(r0->_lo, r1->_lo); + + // To get the number of bits to shift, we count the leading 0-bits and then subtract one, as the sign bit is already set. + // Since count_leading_zeros is undefined at 0 (~(-1)) the number of digits in the native type can be used instead, + // as it returns 31 and 63 for signed integers and longs respectively. + int shift_bits = sel_val == 0 ? std::numeric_limits::digits : count_leading_zeros(sel_val) - 1; + + // Since the result of bitwise-and can be as high as the largest value of each range, the result should find the maximum. + return IntegerType::make(std::numeric_limits::min() >> shift_bits, MAX2(r0->_hi, r1->_hi), widen); +} + //============================================================================= //------------------------------mul_ring--------------------------------------- // Supplied function returns the product of the inputs IN THE CURRENT RING. @@ -601,29 +645,10 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { // This also type-checks the inputs for sanity. Guaranteed never to // be passed a TOP or BOTTOM type, these are filtered out by pre-check. const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const { - const TypeInt *r0 = t0->is_int(); // Handy access - const TypeInt *r1 = t1->is_int(); - int widen = MAX2(r0->_widen,r1->_widen); - - // If either input is a constant, might be able to trim cases - if( !r0->is_con() && !r1->is_con() ) - return TypeInt::INT; // No constants to be had - - // Both constants? Return bits - if( r0->is_con() && r1->is_con() ) - return TypeInt::make( r0->get_con() & r1->get_con() ); - - if( r0->is_con() && r0->get_con() > 0 ) - return TypeInt::make(0, r0->get_con(), widen); - - if( r1->is_con() && r1->get_con() > 0 ) - return TypeInt::make(0, r1->get_con(), widen); + const TypeInt* r0 = t0->is_int(); + const TypeInt* r1 = t1->is_int(); - if( r0 == TypeInt::BOOL || r1 == TypeInt::BOOL ) { - return TypeInt::BOOL; - } - - return TypeInt::INT; // No constants to be had + return and_value(r0, r1); } const Type* AndINode::Value(PhaseGVN* phase) const { @@ -751,25 +776,10 @@ Node *AndINode::Ideal(PhaseGVN *phase, bool can_reshape) { // This also type-checks the inputs for sanity. Guaranteed never to // be passed a TOP or BOTTOM type, these are filtered out by pre-check. const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const { - const TypeLong *r0 = t0->is_long(); // Handy access - const TypeLong *r1 = t1->is_long(); - int widen = MAX2(r0->_widen,r1->_widen); - - // If either input is a constant, might be able to trim cases - if( !r0->is_con() && !r1->is_con() ) - return TypeLong::LONG; // No constants to be had - - // Both constants? Return bits - if( r0->is_con() && r1->is_con() ) - return TypeLong::make( r0->get_con() & r1->get_con() ); - - if( r0->is_con() && r0->get_con() > 0 ) - return TypeLong::make(CONST64(0), r0->get_con(), widen); - - if( r1->is_con() && r1->get_con() > 0 ) - return TypeLong::make(CONST64(0), r1->get_con(), widen); + const TypeLong* r0 = t0->is_long(); + const TypeLong* r1 = t1->is_long(); - return TypeLong::LONG; // No constants to be had + return and_value(r0, r1); } const Type* AndLNode::Value(PhaseGVN* phase) const { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java index f20c28e321db1..5012fab5f2ba6 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java @@ -27,7 +27,8 @@ /* * @test - * @bug 8297384 + * @bug 8297384 8335444 + * @key randomness * @summary Test that Ideal transformations of AndINode* are being performed as expected. * @library /test/lib / * @run driver compiler.c2.irTests.AndINodeIdealizationTests @@ -38,7 +39,7 @@ public static void main(String[] args) { TestFramework.run(); } - @Run(test = { "test1", "test2" }) + @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); int b = RunInfo.getRandom().nextInt(); @@ -47,7 +48,12 @@ public void runMethod() { int max = Integer.MAX_VALUE; assertResult(0, 0); + assertResult(10, 20); + assertResult(10, -20); + assertResult(-10, 20); + assertResult(-10, -20); assertResult(a, b); + assertResult(b, a); assertResult(min, min); assertResult(max, max); } @@ -56,6 +62,13 @@ public void runMethod() { public void assertResult(int a, int b) { Asserts.assertEQ((0 - a) & 1, test1(a)); Asserts.assertEQ((~a) & (~b), test2(a, b)); + Asserts.assertEQ((a & 15) >= 0, test3(a, b)); + Asserts.assertEQ((a & 15) > 15, test4(a, b)); + Asserts.assertEQ((a & (b >>> 1)) >= 0, test5(a, b)); + Asserts.assertEQ((a & (b >>> 30)) > 3, test6(a, b)); + Asserts.assertEQ(((byte)a & -8) >= -128, test7(a, b)); + Asserts.assertEQ(((byte)a & -8) <= 127, test8(a, b)); + Asserts.assertEQ(((a & 255) & (char)b) > 255, test9(a, b)); } @Test @@ -74,4 +87,53 @@ public int test1(int x) { public int test2(int a, int b) { return (~a) & (~b); } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & 15 => [0, 15] + public boolean test3(int a, int b) { + return (a & 15) >= 0; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & 15 => [0, 15] + public boolean test4(int a, int b) { + return (a & 15) > 15; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & [0, int_max] => [0, int_max] + public boolean test5(int a, int b) { + return (a & (b >>> 1)) >= 0; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & [0, 3] => [0, 3] + public boolean test6(int a, int b) { + return (a & (b >>> 30)) > 3; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks [-128, 127] & -8 => [-128, 127] + public boolean test7(int a, int b) { + return ((byte)a & -8) >= -128; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks [-128, 127] & -8 => [-128, 127] + public boolean test8(int a, int b) { + return ((byte)a & -8) <= 127; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks that [0, 255] & [0, 65535] => [0, 255] + public boolean test9(int a, int b) { + return ((a & 255) & (char)b) > 255; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java index 9aa1b62be9743..f80d6e9fef09f 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java @@ -27,7 +27,8 @@ /* * @test - * @bug 8322589 + * @bug 8322589 8335444 + * @key randomness * @summary Test that Ideal transformations of AndLNode* are being performed as expected. * @library /test/lib / * @run driver compiler.c2.irTests.AndLNodeIdealizationTests @@ -38,7 +39,7 @@ public static void main(String[] args) { TestFramework.run(); } - @Run(test = { "test1" }) + @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8" }) public void runMethod() { long a = RunInfo.getRandom().nextLong(); long b = RunInfo.getRandom().nextLong(); @@ -47,7 +48,12 @@ public void runMethod() { long max = Long.MAX_VALUE; assertResult(0, 0); + assertResult(10, 20); + assertResult(10, -20); + assertResult(-10, 20); + assertResult(-10, -20); assertResult(a, b); + assertResult(b, a); assertResult(min, min); assertResult(max, max); } @@ -55,6 +61,13 @@ public void runMethod() { @DontCompile public void assertResult(long a, long b) { Asserts.assertEQ((~a) & (~b), test1(a, b)); + Asserts.assertEQ((a & 15) >= 0, test2(a, b)); + Asserts.assertEQ((a & 15) > 15, test3(a, b)); + Asserts.assertEQ((a & (b >>> 1)) >= 0, test4(a, b)); + Asserts.assertEQ((a & (b >>> 62)) > 3, test5(a, b)); + Asserts.assertEQ(((byte)a & -8L) >= -128, test6(a, b)); + Asserts.assertEQ(((byte)a & -8L) <= 127, test7(a, b)); + Asserts.assertEQ(((a & 255) & (char)b) > 255, test8(a, b)); } @Test @@ -65,4 +78,53 @@ public void assertResult(long a, long b) { public long test1(long a, long b) { return (~a) & (~b); } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & 15 => [0, 15] + public boolean test2(long a, long b) { + return (a & 15) >= 0; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & 15 => [0, 15] + public boolean test3(long a, long b) { + return (a & 15) > 15; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & [0, long_max] => [0, long_max] + public boolean test4(long a, long b) { + return (a & (b >>> 1)) >= 0; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks a & [0, 3] => [0, 3] + public boolean test5(long a, long b) { + return (a & (b >>> 62)) > 3; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks [-128, 127] & -8 => [-128, 127] + public boolean test6(long a, long b) { + return ((byte)a & -8L) >= -128; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks [-128, 127] & -8 => [-128, 127] + public boolean test7(long a, long b) { + return ((byte)a & -8L) <= 127; + } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks that [0, 255] & [0, 65535] => [0, 255] + public boolean test8(long a, long b) { + return ((a & 255) & (char)b) > 255; + } } From 1de4cee92c1c5b46dda7bb8e47ce9659fca4d662 Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Mon, 5 Aug 2024 23:36:47 -0400 Subject: [PATCH 2/4] Fix IR test checks and add benchmark --- .../runner/BasicBooleanOpTest.java | 7 ++----- .../bench/vm/compiler/TypeVectorOperations.java | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java index d1a61538f2e7e..9fb7fe585f3fd 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * Copyright (c) 2024, 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 @@ -71,12 +72,8 @@ public boolean[] vectorNot() { } @Test - @IR(applyIfCPUFeature = {"asimd", "true"}, - counts = {IRNode.AND_VB, ">0"}) - @IR(applyIfCPUFeatureAnd = {"avx512f", "false", "sse2", "true"}, + @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, counts = {IRNode.AND_VB, ">0"}) - @IR(applyIfCPUFeature = {"avx512f", "true"}, - counts = {IRNode.MACRO_LOGIC_V, ">0"}) public boolean[] vectorAnd() { boolean[] res = new boolean[SIZE]; for (int i = 0; i < SIZE; i++) { diff --git a/test/micro/org/openjdk/bench/vm/compiler/TypeVectorOperations.java b/test/micro/org/openjdk/bench/vm/compiler/TypeVectorOperations.java index 9d4f62604409a..a3e4445664ce9 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/TypeVectorOperations.java +++ b/test/micro/org/openjdk/bench/vm/compiler/TypeVectorOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, 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 @@ -38,6 +38,9 @@ public abstract class TypeVectorOperations { @Param({"512", /* "1024", */ "2048"}) public int COUNT; + private boolean[] boolsA; + private boolean[] boolsB; + private boolean[] resZ; private byte[] bytesA; private byte[] bytesB; private byte[] resB; @@ -58,6 +61,9 @@ public abstract class TypeVectorOperations { @Setup public void init() { + boolsA = new boolean[COUNT]; + boolsB = new boolean[COUNT]; + resZ = new boolean[COUNT]; bytesA = new byte[COUNT]; bytesB = new byte[COUNT]; resB = new byte[COUNT]; @@ -73,6 +79,8 @@ public void init() { resF = new float[COUNT]; for (int i = 0; i < COUNT; i++) { + boolsA[i] = r.nextBoolean(); + boolsB[i] = r.nextBoolean(); shorts[i] = (short) r.nextInt(Short.MAX_VALUE + 1); ints[i] = r.nextInt(); longs[i] = r.nextLong(); @@ -366,6 +374,13 @@ public void convertS2L() { } } + @Benchmark + public void andZ() { + for (int i = 0; i < COUNT; i++) { + resZ[i] = boolsA[i] & boolsB[i]; + } + } + @Benchmark @Fork(jvmArgsPrepend = {"-XX:+UseCMoveUnconditionally", "-XX:+UseVectorCmov"}) public void cmoveD() { From ca2db5839a1126232cb08cb3c09ee12bb32acb34 Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Tue, 6 Aug 2024 21:07:51 -0400 Subject: [PATCH 3/4] Check IR before macro expansion --- .../jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java index 9fb7fe585f3fd..bb6185177e75f 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java @@ -73,6 +73,7 @@ public boolean[] vectorNot() { @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, counts = {IRNode.AND_VB, ">0"}) public boolean[] vectorAnd() { boolean[] res = new boolean[SIZE]; From 0d1774232627257989eaebeb3415ef5f0548610a Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> Date: Fri, 6 Sep 2024 00:26:52 -0400 Subject: [PATCH 4/4] Improve cases with two negative ranges, add more documentation --- src/hotspot/share/opto/mulnode.cpp | 45 +++++++++++++------ .../c2/irTests/AndINodeIdealizationTests.java | 10 ++++- .../c2/irTests/AndLNodeIdealizationTests.java | 10 ++++- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 5a60a4f8c91e1..ad98fda025f07 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -597,20 +597,25 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { template static const IntegerType* and_value(const IntegerType* r0, const IntegerType* r1) { typedef typename IntegerType::NativeType NativeType; + static_assert(std::is_signed::value, "Native type of IntegerType must be signed!"); - int widen = MAX2(r0->_widen,r1->_widen); + int widen = MAX2(r0->_widen, r1->_widen); - // If both are constants, we can calculate a precise result. - if(r0->is_con() && r1->is_con()) { + // If both types are constants, we can calculate a constant result. + if (r0->is_con() && r1->is_con()) { return IntegerType::make(r0->get_con() & r1->get_con()); } - // If both ranges are positive, the result will range from 0 up to the maximum value of the smaller range. + // If both ranges are positive, the result will range from 0 up to the hi value of the smaller range. The minimum + // of the two constrains the upper bound because any higher value in the other range will see all zeroes, so it will be masked out. if (r0->_lo >= 0 && r1->_lo >= 0) { return IntegerType::make(0, MIN2(r0->_hi, r1->_hi), widen); } // If only one range is positive, the result will range from 0 up to that range's maximum value. + // For the operation 'x & C' where C is a positive constant, the result will be in the range [0..C]. With that observation, + // we can say that for any integer c such that 0 <= c <= C will also be in the range [0..C]. Therefore, 'x & [c..C]' + // where c >= 0 will be in the range [0..C]. if (r0->_lo >= 0) { return IntegerType::make(0, r0->_hi, widen); } @@ -623,19 +628,33 @@ static const IntegerType* and_value(const IntegerType* r0, const IntegerType* r1 // and constants. assert(r0->_lo < 0 && r1->_lo < 0, "positive ranges should already be handled!"); - static_assert(std::is_signed::value, "native type of IntegerType must be signed!"); - // The lower bound of both ranges will contain the common leading 1-bits. In order to count them with count_leading_zeros, - // the bits are inverted. + // As two's complement means that both numbers will start with leading 1s, the lower bound of both ranges will contain + // the common leading 1s of both minimum values. In order to count them with count_leading_zeros, the bits are inverted. NativeType sel_val = ~MIN2(r0->_lo, r1->_lo); - // To get the number of bits to shift, we count the leading 0-bits and then subtract one, as the sign bit is already set. - // Since count_leading_zeros is undefined at 0 (~(-1)) the number of digits in the native type can be used instead, - // as it returns 31 and 63 for signed integers and longs respectively. - int shift_bits = sel_val == 0 ? std::numeric_limits::digits : count_leading_zeros(sel_val) - 1; + NativeType min; + if (sel_val == 0) { + // Since count_leading_zeros is undefined at 0, we short-circuit the condition where both ranges have a minimum of -1. + min = -1; + } else { + // To get the number of bits to shift, we count the leading 0-bits and then subtract one, as the sign bit is already set. + int shift_bits = count_leading_zeros(sel_val) - 1; + min = std::numeric_limits::min() >> shift_bits; + } + + NativeType max; + if (r0->_hi < 0 && r1->_hi < 0) { + // If both ranges are negative, then the same optimization as both positive ranges will apply, and the smaller hi + // value will mask off any bits set by higher values. + max = MIN2(r0->_hi, r1->_hi); + } else { + // In the case of ranges that cross zero, negative values can cause the higher order bits to be set, so the maximum + // positive value can be as high as the larger hi value. + max = MAX2(r0->_hi, r1->_hi); + } - // Since the result of bitwise-and can be as high as the largest value of each range, the result should find the maximum. - return IntegerType::make(std::numeric_limits::min() >> shift_bits, MAX2(r0->_hi, r1->_hi), widen); + return IntegerType::make(min, max, widen); } //============================================================================= diff --git a/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java index 5012fab5f2ba6..1e90c22ddabc3 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java @@ -39,7 +39,7 @@ public static void main(String[] args) { TestFramework.run(); } - @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" }) + @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10" }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); int b = RunInfo.getRandom().nextInt(); @@ -69,6 +69,7 @@ public void assertResult(int a, int b) { Asserts.assertEQ(((byte)a & -8) >= -128, test7(a, b)); Asserts.assertEQ(((byte)a & -8) <= 127, test8(a, b)); Asserts.assertEQ(((a & 255) & (char)b) > 255, test9(a, b)); + Asserts.assertEQ((((a & 1) - 3) & ((b & 2) - 10)) > -8, test10(a, b)); } @Test @@ -136,4 +137,11 @@ public boolean test8(int a, int b) { public boolean test9(int a, int b) { return ((a & 255) & (char)b) > 255; } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks that [-3, -2] & [-10, -8] => [-16, -8] + public boolean test10(int a, int b) { + return (((a & 1) - 3) & ((b & 2) - 10)) > -8; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java index f80d6e9fef09f..a32172b20153a 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java @@ -39,7 +39,7 @@ public static void main(String[] args) { TestFramework.run(); } - @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8" }) + @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" }) public void runMethod() { long a = RunInfo.getRandom().nextLong(); long b = RunInfo.getRandom().nextLong(); @@ -68,6 +68,7 @@ public void assertResult(long a, long b) { Asserts.assertEQ(((byte)a & -8L) >= -128, test6(a, b)); Asserts.assertEQ(((byte)a & -8L) <= 127, test7(a, b)); Asserts.assertEQ(((a & 255) & (char)b) > 255, test8(a, b)); + Asserts.assertEQ((((a & 1) - 3) & ((b & 2) - 10)) > -8, test9(a, b)); } @Test @@ -127,4 +128,11 @@ public boolean test7(long a, long b) { public boolean test8(long a, long b) { return ((a & 255) & (char)b) > 255; } + + @Test + @IR(failOn = { IRNode.AND }) + // Checks that [-3, -2] & [-10, -8] => [-16, -8] + public boolean test9(long a, long b) { + return (((a & 1) - 3) & ((b & 2) - 10)) > -8; + } }