Skip to content

Commit 9243104

Browse files
committed
8335444: Generalize implementation of AndNode mul_ring
Reviewed-by: chagedorn, qamai, dfenacci
1 parent 3352522 commit 9243104

File tree

5 files changed

+232
-50
lines changed

5 files changed

+232
-50
lines changed

src/hotspot/share/opto/mulnode.cpp

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -594,36 +594,80 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) {
594594
return TypeLong::LONG;
595595
}
596596

597+
template<typename IntegerType>
598+
static const IntegerType* and_value(const IntegerType* r0, const IntegerType* r1) {
599+
typedef typename IntegerType::NativeType NativeType;
600+
static_assert(std::is_signed<NativeType>::value, "Native type of IntegerType must be signed!");
601+
602+
int widen = MAX2(r0->_widen, r1->_widen);
603+
604+
// If both types are constants, we can calculate a constant result.
605+
if (r0->is_con() && r1->is_con()) {
606+
return IntegerType::make(r0->get_con() & r1->get_con());
607+
}
608+
609+
// If both ranges are positive, the result will range from 0 up to the hi value of the smaller range. The minimum
610+
// 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.
611+
if (r0->_lo >= 0 && r1->_lo >= 0) {
612+
return IntegerType::make(0, MIN2(r0->_hi, r1->_hi), widen);
613+
}
614+
615+
// If only one range is positive, the result will range from 0 up to that range's maximum value.
616+
// For the operation 'x & C' where C is a positive constant, the result will be in the range [0..C]. With that observation,
617+
// 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]'
618+
// where c >= 0 will be in the range [0..C].
619+
if (r0->_lo >= 0) {
620+
return IntegerType::make(0, r0->_hi, widen);
621+
}
622+
623+
if (r1->_lo >= 0) {
624+
return IntegerType::make(0, r1->_hi, widen);
625+
}
626+
627+
// At this point, all positive ranges will have already been handled, so the only remaining cases will be negative ranges
628+
// and constants.
629+
630+
assert(r0->_lo < 0 && r1->_lo < 0, "positive ranges should already be handled!");
631+
632+
// As two's complement means that both numbers will start with leading 1s, the lower bound of both ranges will contain
633+
// the common leading 1s of both minimum values. In order to count them with count_leading_zeros, the bits are inverted.
634+
NativeType sel_val = ~MIN2(r0->_lo, r1->_lo);
635+
636+
NativeType min;
637+
if (sel_val == 0) {
638+
// Since count_leading_zeros is undefined at 0, we short-circuit the condition where both ranges have a minimum of -1.
639+
min = -1;
640+
} else {
641+
// 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.
642+
int shift_bits = count_leading_zeros(sel_val) - 1;
643+
min = std::numeric_limits<NativeType>::min() >> shift_bits;
644+
}
645+
646+
NativeType max;
647+
if (r0->_hi < 0 && r1->_hi < 0) {
648+
// If both ranges are negative, then the same optimization as both positive ranges will apply, and the smaller hi
649+
// value will mask off any bits set by higher values.
650+
max = MIN2(r0->_hi, r1->_hi);
651+
} else {
652+
// In the case of ranges that cross zero, negative values can cause the higher order bits to be set, so the maximum
653+
// positive value can be as high as the larger hi value.
654+
max = MAX2(r0->_hi, r1->_hi);
655+
}
656+
657+
return IntegerType::make(min, max, widen);
658+
}
659+
597660
//=============================================================================
598661
//------------------------------mul_ring---------------------------------------
599662
// Supplied function returns the product of the inputs IN THE CURRENT RING.
600663
// For the logical operations the ring's MUL is really a logical AND function.
601664
// This also type-checks the inputs for sanity. Guaranteed never to
602665
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
603666
const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const {
604-
const TypeInt *r0 = t0->is_int(); // Handy access
605-
const TypeInt *r1 = t1->is_int();
606-
int widen = MAX2(r0->_widen,r1->_widen);
607-
608-
// If either input is a constant, might be able to trim cases
609-
if( !r0->is_con() && !r1->is_con() )
610-
return TypeInt::INT; // No constants to be had
611-
612-
// Both constants? Return bits
613-
if( r0->is_con() && r1->is_con() )
614-
return TypeInt::make( r0->get_con() & r1->get_con() );
615-
616-
if( r0->is_con() && r0->get_con() > 0 )
617-
return TypeInt::make(0, r0->get_con(), widen);
667+
const TypeInt* r0 = t0->is_int();
668+
const TypeInt* r1 = t1->is_int();
618669

619-
if( r1->is_con() && r1->get_con() > 0 )
620-
return TypeInt::make(0, r1->get_con(), widen);
621-
622-
if( r0 == TypeInt::BOOL || r1 == TypeInt::BOOL ) {
623-
return TypeInt::BOOL;
624-
}
625-
626-
return TypeInt::INT; // No constants to be had
670+
return and_value<TypeInt>(r0, r1);
627671
}
628672

629673
const Type* AndINode::Value(PhaseGVN* phase) const {
@@ -751,25 +795,10 @@ Node *AndINode::Ideal(PhaseGVN *phase, bool can_reshape) {
751795
// This also type-checks the inputs for sanity. Guaranteed never to
752796
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
753797
const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const {
754-
const TypeLong *r0 = t0->is_long(); // Handy access
755-
const TypeLong *r1 = t1->is_long();
756-
int widen = MAX2(r0->_widen,r1->_widen);
757-
758-
// If either input is a constant, might be able to trim cases
759-
if( !r0->is_con() && !r1->is_con() )
760-
return TypeLong::LONG; // No constants to be had
761-
762-
// Both constants? Return bits
763-
if( r0->is_con() && r1->is_con() )
764-
return TypeLong::make( r0->get_con() & r1->get_con() );
765-
766-
if( r0->is_con() && r0->get_con() > 0 )
767-
return TypeLong::make(CONST64(0), r0->get_con(), widen);
768-
769-
if( r1->is_con() && r1->get_con() > 0 )
770-
return TypeLong::make(CONST64(0), r1->get_con(), widen);
798+
const TypeLong* r0 = t0->is_long();
799+
const TypeLong* r1 = t1->is_long();
771800

772-
return TypeLong::LONG; // No constants to be had
801+
return and_value<TypeLong>(r0, r1);
773802
}
774803

775804
const Type* AndLNode::Value(PhaseGVN* phase) const {

test/hotspot/jtreg/compiler/c2/irTests/AndINodeIdealizationTests.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727

2828
/*
2929
* @test
30-
* @bug 8297384
30+
* @bug 8297384 8335444
31+
* @key randomness
3132
* @summary Test that Ideal transformations of AndINode* are being performed as expected.
3233
* @library /test/lib /
3334
* @run driver compiler.c2.irTests.AndINodeIdealizationTests
@@ -38,7 +39,7 @@ public static void main(String[] args) {
3839
TestFramework.run();
3940
}
4041

41-
@Run(test = { "test1", "test2" })
42+
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10" })
4243
public void runMethod() {
4344
int a = RunInfo.getRandom().nextInt();
4445
int b = RunInfo.getRandom().nextInt();
@@ -47,7 +48,12 @@ public void runMethod() {
4748
int max = Integer.MAX_VALUE;
4849

4950
assertResult(0, 0);
51+
assertResult(10, 20);
52+
assertResult(10, -20);
53+
assertResult(-10, 20);
54+
assertResult(-10, -20);
5055
assertResult(a, b);
56+
assertResult(b, a);
5157
assertResult(min, min);
5258
assertResult(max, max);
5359
}
@@ -56,6 +62,14 @@ public void runMethod() {
5662
public void assertResult(int a, int b) {
5763
Asserts.assertEQ((0 - a) & 1, test1(a));
5864
Asserts.assertEQ((~a) & (~b), test2(a, b));
65+
Asserts.assertEQ((a & 15) >= 0, test3(a, b));
66+
Asserts.assertEQ((a & 15) > 15, test4(a, b));
67+
Asserts.assertEQ((a & (b >>> 1)) >= 0, test5(a, b));
68+
Asserts.assertEQ((a & (b >>> 30)) > 3, test6(a, b));
69+
Asserts.assertEQ(((byte)a & -8) >= -128, test7(a, b));
70+
Asserts.assertEQ(((byte)a & -8) <= 127, test8(a, b));
71+
Asserts.assertEQ(((a & 255) & (char)b) > 255, test9(a, b));
72+
Asserts.assertEQ((((a & 1) - 3) & ((b & 2) - 10)) > -8, test10(a, b));
5973
}
6074

6175
@Test
@@ -74,4 +88,60 @@ public int test1(int x) {
7488
public int test2(int a, int b) {
7589
return (~a) & (~b);
7690
}
91+
92+
@Test
93+
@IR(failOn = { IRNode.AND })
94+
// Checks a & 15 => [0, 15]
95+
public boolean test3(int a, int b) {
96+
return (a & 15) >= 0;
97+
}
98+
99+
@Test
100+
@IR(failOn = { IRNode.AND })
101+
// Checks a & 15 => [0, 15]
102+
public boolean test4(int a, int b) {
103+
return (a & 15) > 15;
104+
}
105+
106+
@Test
107+
@IR(failOn = { IRNode.AND })
108+
// Checks a & [0, int_max] => [0, int_max]
109+
public boolean test5(int a, int b) {
110+
return (a & (b >>> 1)) >= 0;
111+
}
112+
113+
@Test
114+
@IR(failOn = { IRNode.AND })
115+
// Checks a & [0, 3] => [0, 3]
116+
public boolean test6(int a, int b) {
117+
return (a & (b >>> 30)) > 3;
118+
}
119+
120+
@Test
121+
@IR(failOn = { IRNode.AND })
122+
// Checks [-128, 127] & -8 => [-128, 127]
123+
public boolean test7(int a, int b) {
124+
return ((byte)a & -8) >= -128;
125+
}
126+
127+
@Test
128+
@IR(failOn = { IRNode.AND })
129+
// Checks [-128, 127] & -8 => [-128, 127]
130+
public boolean test8(int a, int b) {
131+
return ((byte)a & -8) <= 127;
132+
}
133+
134+
@Test
135+
@IR(failOn = { IRNode.AND })
136+
// Checks that [0, 255] & [0, 65535] => [0, 255]
137+
public boolean test9(int a, int b) {
138+
return ((a & 255) & (char)b) > 255;
139+
}
140+
141+
@Test
142+
@IR(failOn = { IRNode.AND })
143+
// Checks that [-3, -2] & [-10, -8] => [-16, -8]
144+
public boolean test10(int a, int b) {
145+
return (((a & 1) - 3) & ((b & 2) - 10)) > -8;
146+
}
77147
}

test/hotspot/jtreg/compiler/c2/irTests/AndLNodeIdealizationTests.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727

2828
/*
2929
* @test
30-
* @bug 8322589
30+
* @bug 8322589 8335444
31+
* @key randomness
3132
* @summary Test that Ideal transformations of AndLNode* are being performed as expected.
3233
* @library /test/lib /
3334
* @run driver compiler.c2.irTests.AndLNodeIdealizationTests
@@ -38,7 +39,7 @@ public static void main(String[] args) {
3839
TestFramework.run();
3940
}
4041

41-
@Run(test = { "test1" })
42+
@Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" })
4243
public void runMethod() {
4344
long a = RunInfo.getRandom().nextLong();
4445
long b = RunInfo.getRandom().nextLong();
@@ -47,14 +48,27 @@ public void runMethod() {
4748
long max = Long.MAX_VALUE;
4849

4950
assertResult(0, 0);
51+
assertResult(10, 20);
52+
assertResult(10, -20);
53+
assertResult(-10, 20);
54+
assertResult(-10, -20);
5055
assertResult(a, b);
56+
assertResult(b, a);
5157
assertResult(min, min);
5258
assertResult(max, max);
5359
}
5460

5561
@DontCompile
5662
public void assertResult(long a, long b) {
5763
Asserts.assertEQ((~a) & (~b), test1(a, b));
64+
Asserts.assertEQ((a & 15) >= 0, test2(a, b));
65+
Asserts.assertEQ((a & 15) > 15, test3(a, b));
66+
Asserts.assertEQ((a & (b >>> 1)) >= 0, test4(a, b));
67+
Asserts.assertEQ((a & (b >>> 62)) > 3, test5(a, b));
68+
Asserts.assertEQ(((byte)a & -8L) >= -128, test6(a, b));
69+
Asserts.assertEQ(((byte)a & -8L) <= 127, test7(a, b));
70+
Asserts.assertEQ(((a & 255) & (char)b) > 255, test8(a, b));
71+
Asserts.assertEQ((((a & 1) - 3) & ((b & 2) - 10)) > -8, test9(a, b));
5872
}
5973

6074
@Test
@@ -65,4 +79,60 @@ public void assertResult(long a, long b) {
6579
public long test1(long a, long b) {
6680
return (~a) & (~b);
6781
}
82+
83+
@Test
84+
@IR(failOn = { IRNode.AND })
85+
// Checks a & 15 => [0, 15]
86+
public boolean test2(long a, long b) {
87+
return (a & 15) >= 0;
88+
}
89+
90+
@Test
91+
@IR(failOn = { IRNode.AND })
92+
// Checks a & 15 => [0, 15]
93+
public boolean test3(long a, long b) {
94+
return (a & 15) > 15;
95+
}
96+
97+
@Test
98+
@IR(failOn = { IRNode.AND })
99+
// Checks a & [0, long_max] => [0, long_max]
100+
public boolean test4(long a, long b) {
101+
return (a & (b >>> 1)) >= 0;
102+
}
103+
104+
@Test
105+
@IR(failOn = { IRNode.AND })
106+
// Checks a & [0, 3] => [0, 3]
107+
public boolean test5(long a, long b) {
108+
return (a & (b >>> 62)) > 3;
109+
}
110+
111+
@Test
112+
@IR(failOn = { IRNode.AND })
113+
// Checks [-128, 127] & -8 => [-128, 127]
114+
public boolean test6(long a, long b) {
115+
return ((byte)a & -8L) >= -128;
116+
}
117+
118+
@Test
119+
@IR(failOn = { IRNode.AND })
120+
// Checks [-128, 127] & -8 => [-128, 127]
121+
public boolean test7(long a, long b) {
122+
return ((byte)a & -8L) <= 127;
123+
}
124+
125+
@Test
126+
@IR(failOn = { IRNode.AND })
127+
// Checks that [0, 255] & [0, 65535] => [0, 255]
128+
public boolean test8(long a, long b) {
129+
return ((a & 255) & (char)b) > 255;
130+
}
131+
132+
@Test
133+
@IR(failOn = { IRNode.AND })
134+
// Checks that [-3, -2] & [-10, -8] => [-16, -8]
135+
public boolean test9(long a, long b) {
136+
return (((a & 1) - 3) & ((b & 2) - 10)) > -8;
137+
}
68138
}

test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2022, 2023, Arm Limited. All rights reserved.
3+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
34
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45
*
56
* This code is free software; you can redistribute it and/or modify it
@@ -71,12 +72,9 @@ public boolean[] vectorNot() {
7172
}
7273

7374
@Test
74-
@IR(applyIfCPUFeature = {"asimd", "true"},
75-
counts = {IRNode.AND_VB, ">0"})
76-
@IR(applyIfCPUFeatureAnd = {"avx512f", "false", "sse2", "true"},
75+
@IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true"},
76+
phase = CompilePhase.BEFORE_MACRO_EXPANSION,
7777
counts = {IRNode.AND_VB, ">0"})
78-
@IR(applyIfCPUFeature = {"avx512f", "true"},
79-
counts = {IRNode.MACRO_LOGIC_V, ">0"})
8078
public boolean[] vectorAnd() {
8179
boolean[] res = new boolean[SIZE];
8280
for (int i = 0; i < SIZE; i++) {

0 commit comments

Comments
 (0)