8213419: C2 may hang in MulLNode::Ideal()/MulINode::Ideal() with gcc 8.2.1
Reviewed-by: kvn, dlong, aph
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp Tue Nov 20 20:00:15 2018 -0800
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp Wed Nov 14 13:15:54 2018 +0100
@@ -317,29 +317,6 @@
enum operation { uxtb, uxth, uxtw, uxtx, sxtb, sxth, sxtw, sxtx };
};
-// abs methods which cannot overflow and so are well-defined across
-// the entire domain of integer types.
-static inline unsigned int uabs(unsigned int n) {
- union {
- unsigned int result;
- int value;
- };
- result = n;
- if (value < 0) result = -result;
- return result;
-}
-static inline unsigned long uabs(unsigned long n) {
- union {
- unsigned long result;
- long value;
- };
- result = n;
- if (value < 0) result = -result;
- return result;
-}
-static inline unsigned long uabs(long n) { return uabs((unsigned long)n); }
-static inline unsigned long uabs(int n) { return uabs((unsigned int)n); }
-
// Addressing modes
class Address {
public:
--- a/src/hotspot/share/opto/mulnode.cpp Tue Nov 20 20:00:15 2018 -0800
+++ b/src/hotspot/share/opto/mulnode.cpp Wed Nov 14 13:15:54 2018 +0100
@@ -170,7 +170,6 @@
return mul_ring(t1,t2); // Local flavor of type multiplication
}
-
//=============================================================================
//------------------------------Ideal------------------------------------------
// Check for power-of-2 multiply, then try the regular MulNode::Ideal
@@ -185,42 +184,43 @@
}
// Now we have a constant Node on the right and the constant in con
- if( con == 0 ) return NULL; // By zero is handled by Value call
- if( con == 1 ) return NULL; // By one is handled by Identity call
+ if (con == 0) return NULL; // By zero is handled by Value call
+ if (con == 1) return NULL; // By one is handled by Identity call
// Check for negative constant; if so negate the final result
bool sign_flip = false;
- if( con < 0 ) {
- con = -con;
+
+ unsigned int abs_con = uabs(con);
+ if (abs_con != (unsigned int)con) {
sign_flip = true;
}
// Get low bit; check for being the only bit
Node *res = NULL;
- jint bit1 = con & -con; // Extract low bit
- if( bit1 == con ) { // Found a power of 2?
- res = new LShiftINode( in(1), phase->intcon(log2_intptr(bit1)) );
+ unsigned int bit1 = abs_con & (0-abs_con); // Extract low bit
+ if (bit1 == abs_con) { // Found a power of 2?
+ res = new LShiftINode(in(1), phase->intcon(log2_intptr(bit1)));
} else {
// Check for constant with 2 bits set
- jint bit2 = con-bit1;
- bit2 = bit2 & -bit2; // Extract 2nd bit
- if( bit2 + bit1 == con ) { // Found all bits in con?
- Node *n1 = phase->transform( new LShiftINode( in(1), phase->intcon(log2_intptr(bit1)) ) );
- Node *n2 = phase->transform( new LShiftINode( in(1), phase->intcon(log2_intptr(bit2)) ) );
- res = new AddINode( n2, n1 );
+ unsigned int bit2 = abs_con-bit1;
+ bit2 = bit2 & (0-bit2); // Extract 2nd bit
+ if (bit2 + bit1 == abs_con) { // Found all bits in con?
+ Node *n1 = phase->transform( new LShiftINode(in(1), phase->intcon(log2_intptr(bit1))));
+ Node *n2 = phase->transform( new LShiftINode(in(1), phase->intcon(log2_intptr(bit2))));
+ res = new AddINode(n2, n1);
- } else if (is_power_of_2(con+1)) {
+ } else if (is_power_of_2(abs_con+1)) {
// Sleezy: power-of-2 -1. Next time be generic.
- jint temp = (jint) (con + 1);
- Node *n1 = phase->transform( new LShiftINode( in(1), phase->intcon(log2_intptr(temp)) ) );
- res = new SubINode( n1, in(1) );
+ unsigned int temp = abs_con + 1;
+ Node *n1 = phase->transform(new LShiftINode(in(1), phase->intcon(log2_intptr(temp))));
+ res = new SubINode(n1, in(1));
} else {
return MulNode::Ideal(phase, can_reshape);
}
}
- if( sign_flip ) { // Need to negate result?
+ if (sign_flip) { // Need to negate result?
res = phase->transform(res);// Transform, before making the zero con
res = new SubINode(phase->intcon(0),res);
}
@@ -281,42 +281,42 @@
}
// Now we have a constant Node on the right and the constant in con
- if( con == CONST64(0) ) return NULL; // By zero is handled by Value call
- if( con == CONST64(1) ) return NULL; // By one is handled by Identity call
+ if (con == CONST64(0)) return NULL; // By zero is handled by Value call
+ if (con == CONST64(1)) return NULL; // By one is handled by Identity call
// Check for negative constant; if so negate the final result
bool sign_flip = false;
- if( con < 0 ) {
- con = -con;
+ unsigned long abs_con = uabs(con);
+ if (abs_con != (unsigned long)con) {
sign_flip = true;
}
// Get low bit; check for being the only bit
Node *res = NULL;
- jlong bit1 = con & -con; // Extract low bit
- if( bit1 == con ) { // Found a power of 2?
- res = new LShiftLNode( in(1), phase->intcon(log2_long(bit1)) );
+ unsigned long bit1 = abs_con & (0-abs_con); // Extract low bit
+ if (bit1 == abs_con) { // Found a power of 2?
+ res = new LShiftLNode(in(1), phase->intcon(log2_long(bit1)));
} else {
// Check for constant with 2 bits set
- jlong bit2 = con-bit1;
- bit2 = bit2 & -bit2; // Extract 2nd bit
- if( bit2 + bit1 == con ) { // Found all bits in con?
- Node *n1 = phase->transform( new LShiftLNode( in(1), phase->intcon(log2_long(bit1)) ) );
- Node *n2 = phase->transform( new LShiftLNode( in(1), phase->intcon(log2_long(bit2)) ) );
- res = new AddLNode( n2, n1 );
+ unsigned long bit2 = abs_con-bit1;
+ bit2 = bit2 & (0-bit2); // Extract 2nd bit
+ if (bit2 + bit1 == abs_con) { // Found all bits in con?
+ Node *n1 = phase->transform(new LShiftLNode(in(1), phase->intcon(log2_long(bit1))));
+ Node *n2 = phase->transform(new LShiftLNode(in(1), phase->intcon(log2_long(bit2))));
+ res = new AddLNode(n2, n1);
- } else if (is_power_of_2_long(con+1)) {
+ } else if (is_power_of_2_long(abs_con+1)) {
// Sleezy: power-of-2 -1. Next time be generic.
- jlong temp = (jlong) (con + 1);
- Node *n1 = phase->transform( new LShiftLNode( in(1), phase->intcon(log2_long(temp)) ) );
- res = new SubLNode( n1, in(1) );
+ unsigned long temp = abs_con + 1;
+ Node *n1 = phase->transform( new LShiftLNode(in(1), phase->intcon(log2_long(temp))));
+ res = new SubLNode(n1, in(1));
} else {
return MulNode::Ideal(phase, can_reshape);
}
}
- if( sign_flip ) { // Need to negate result?
+ if (sign_flip) { // Need to negate result?
res = phase->transform(res);// Transform, before making the zero con
res = new SubLNode(phase->longcon(0),res);
}
--- a/src/hotspot/share/utilities/globalDefinitions.hpp Tue Nov 20 20:00:15 2018 -0800
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp Wed Nov 14 13:15:54 2018 +0100
@@ -1034,10 +1034,10 @@
// Returns largest i such that 2^i <= x.
// If x < 0, the function returns 31 on a 32-bit machine and 63 on a 64-bit machine.
// If x == 0, the function returns -1.
-inline int log2_intptr(intptr_t x) {
+inline int log2_intptr(uintptr_t x) {
int i = -1;
uintptr_t p = 1;
- while (p != 0 && p <= (uintptr_t)x) {
+ while (p != 0 && p <= x) {
// p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x)
i++; p *= 2;
}
@@ -1048,10 +1048,10 @@
//* largest i such that 2^i <= x
// A negative value of 'x' will return '63'
-inline int log2_long(jlong x) {
+inline int log2_long(unsigned long x) {
int i = -1;
julong p = 1;
- while (p != 0 && p <= (julong)x) {
+ while (p != 0 && p <= x) {
// p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x)
i++; p *= 2;
}
@@ -1060,6 +1060,22 @@
return i;
}
+inline int log2_intptr(intptr_t x) {
+ return log2_intptr((uintptr_t)x);
+}
+
+inline int log2_intptr(int x) {
+ return log2_intptr((uintptr_t)x);
+}
+
+inline int log2_intptr(uint x) {
+ return log2_intptr((uintptr_t)x);
+}
+
+inline int log2_long(jlong x) {
+ return log2_long((unsigned long)x);
+}
+
//* the argument must be exactly a power of 2
inline int exact_log2(intptr_t x) {
assert(is_power_of_2(x), "x must be a power of 2: " INTPTR_FORMAT, x);
@@ -1075,6 +1091,29 @@
inline bool is_odd (intx x) { return x & 1; }
inline bool is_even(intx x) { return !is_odd(x); }
+// abs methods which cannot overflow and so are well-defined across
+// the entire domain of integer types.
+static inline unsigned int uabs(unsigned int n) {
+ union {
+ unsigned int result;
+ int value;
+ };
+ result = n;
+ if (value < 0) result = 0-result;
+ return result;
+}
+static inline unsigned long uabs(unsigned long n) {
+ union {
+ unsigned long result;
+ long value;
+ };
+ result = n;
+ if (value < 0) result = 0-result;
+ return result;
+}
+static inline unsigned long uabs(jlong n) { return uabs((unsigned long)n); }
+static inline unsigned int uabs(int n) { return uabs((unsigned int)n); }
+
// "to" should be greater than "from."
inline intx byte_size(void* from, void* to) {
return (address)to - (address)from;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/integerArithmetic/MultiplyByIntegerMinHang.java Wed Nov 14 13:15:54 2018 +0100
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018, Red Hat, Inc. 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 8213419
+ * @summary C2 may hang in MulLNode::Ideal()/MulINode::Ideal() with gcc 8.2.1
+ *
+ * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation -XX:-UseOnStackReplacement MultiplyByIntegerMinHang
+ *
+ */
+
+public class MultiplyByIntegerMinHang {
+ public static void main(String[] args) {
+ for (int i = 0; i < 20_000; i++) {
+ if (test1(0) != 0) {
+ throw new RuntimeException("incorrect result");
+ }
+ if (test1(1) != Integer.MIN_VALUE) {
+ throw new RuntimeException("incorrect result");
+ }
+ if (test1(2) != 0) {
+ throw new RuntimeException("incorrect result");
+ }
+ if (test2(0) != 0) {
+ throw new RuntimeException("incorrect result");
+ }
+ if (test2(1) != Long.MIN_VALUE) {
+ throw new RuntimeException("incorrect result");
+ }
+ if (test2(2) != 0) {
+ throw new RuntimeException("incorrect result");
+ }
+ }
+ }
+
+ private static int test1(int v) {
+ return v * Integer.MIN_VALUE;
+ }
+
+ private static long test2(long v) {
+ return v * Long.MIN_VALUE;
+ }
+}