# HG changeset patch # User sangheki # Date 1503519659 0 # Node ID 61273934927909b735b202305604d4d605c56f34 # Parent 9d444ac611a9b99bcf246409d49cb89578cac401# Parent 7bdccf51db41746f05ba452f481d554f2219a0b9 Merge diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java Wed Aug 23 20:20:59 2017 +0000 @@ -158,7 +158,7 @@ } break; default: - throw GraalError.shouldNotReachHere(); + throw GraalError.shouldNotReachHere(input.getPlatformKind().toString()); } return result; } @@ -451,7 +451,7 @@ protected Value emitZeroExtendMemory(AMD64Kind memoryKind, int resultBits, AMD64AddressValue address, LIRFrameState state) { // Issue a zero extending load of the proper bit size and set the result to // the proper kind. - Variable result = getLIRGen().newVariable(LIRKind.value(resultBits == 32 ? AMD64Kind.DWORD : AMD64Kind.QWORD)); + Variable result = getLIRGen().newVariable(LIRKind.value(resultBits <= 32 ? AMD64Kind.DWORD : AMD64Kind.QWORD)); switch (memoryKind) { case BYTE: getLIRGen().append(new AMD64Unary.MemoryOp(MOVZXB, DWORD, result, address, state)); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvert.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,16 +25,26 @@ import org.graalvm.compiler.debug.GraalError; public enum FloatConvert { - F2I, - D2I, - F2L, - D2L, - I2F, - L2F, - D2F, - I2D, - L2D, - F2D; + F2I(FloatConvertCategory.FloatingPointToInteger), + D2I(FloatConvertCategory.FloatingPointToInteger), + F2L(FloatConvertCategory.FloatingPointToInteger), + D2L(FloatConvertCategory.FloatingPointToInteger), + I2F(FloatConvertCategory.IntegerToFloatingPoint), + L2F(FloatConvertCategory.IntegerToFloatingPoint), + D2F(FloatConvertCategory.FloatingPointToFloatingPoint), + I2D(FloatConvertCategory.IntegerToFloatingPoint), + L2D(FloatConvertCategory.IntegerToFloatingPoint), + F2D(FloatConvertCategory.FloatingPointToFloatingPoint); + + private FloatConvertCategory category; + + FloatConvert(FloatConvertCategory category) { + this.category = category; + } + + public FloatConvertCategory getCategory() { + return category; + } public FloatConvert reverse() { switch (this) { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvertCategory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/FloatConvertCategory.java Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.core.common.calc; + +public enum FloatConvertCategory { + FloatingPointToInteger, + IntegerToFloatingPoint, + FloatingPointToFloatingPoint; +} diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java Wed Aug 23 20:20:59 2017 +0000 @@ -33,9 +33,11 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Div; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.MulHigh; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Sub; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.UMulHigh; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Xor; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.Narrow; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp.SignExtend; @@ -62,6 +64,8 @@ private final BinaryOp sub; private final BinaryOp mul; + private final BinaryOp mulHigh; + private final BinaryOp umulHigh; private final BinaryOp
div; private final BinaryOp rem; @@ -92,7 +96,7 @@ } } - public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + public static final ArithmeticOpTable EMPTY = new ArithmeticOpTable(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); public interface ArithmeticOpWrapper { @@ -121,6 +125,8 @@ BinaryOp sub = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getSub()); BinaryOp mul = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMul()); + BinaryOp mulHigh = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getMulHigh()); + BinaryOp umulHigh = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getUMulHigh()); BinaryOp
div = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getDiv()); BinaryOp rem = wrapIfNonNull(wrapper::wrapBinaryOp, inner.getRem()); @@ -141,16 +147,18 @@ IntegerConvertOp narrow = wrapIfNonNull(wrapper::wrapIntegerConvertOp, inner.getNarrow()); FloatConvertOp[] floatConvert = CollectionsUtil.filterAndMapToArray(inner.floatConvert, Objects::nonNull, wrapper::wrapFloatConvertOp, FloatConvertOp[]::new); - return new ArithmeticOpTable(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert); + return new ArithmeticOpTable(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow, floatConvert); } - protected ArithmeticOpTable(UnaryOp neg, BinaryOp add, BinaryOp sub, BinaryOp mul, BinaryOp
div, BinaryOp rem, UnaryOp not, BinaryOp and, BinaryOp or, - BinaryOp xor, ShiftOp shl, ShiftOp shr, ShiftOp ushr, UnaryOp abs, UnaryOp sqrt, IntegerConvertOp zeroExtend, - IntegerConvertOp signExtend, IntegerConvertOp narrow, FloatConvertOp... floatConvert) { + protected ArithmeticOpTable(UnaryOp neg, BinaryOp add, BinaryOp sub, BinaryOp mul, BinaryOp mulHigh, BinaryOp umulHigh, BinaryOp
div, BinaryOp rem, + UnaryOp not, BinaryOp and, BinaryOp or, BinaryOp xor, ShiftOp shl, ShiftOp shr, ShiftOp ushr, UnaryOp abs, UnaryOp sqrt, + IntegerConvertOp zeroExtend, IntegerConvertOp signExtend, IntegerConvertOp narrow, FloatConvertOp... floatConvert) { this.neg = neg; this.add = add; this.sub = sub; this.mul = mul; + this.mulHigh = mulHigh; + this.umulHigh = umulHigh; this.div = div; this.rem = rem; this.not = not; @@ -207,6 +215,20 @@ } /** + * Describes a signed operation that multiples the upper 32-bits of two long values. + */ + public BinaryOp getMulHigh() { + return mulHigh; + } + + /** + * Describes an unsigned operation that multiples the upper 32-bits of two long values. + */ + public BinaryOp getUMulHigh() { + return umulHigh; + } + + /** * Describes the division operation. */ public BinaryOp
getDiv() { @@ -321,6 +343,8 @@ Objects.equals(add, that.add) && Objects.equals(sub, that.sub) && Objects.equals(mul, that.mul) && + Objects.equals(mulHigh, that.mulHigh) && + Objects.equals(umulHigh, that.umulHigh) && Objects.equals(div, that.div) && Objects.equals(rem, that.rem) && Objects.equals(not, that.not) && @@ -360,8 +384,8 @@ @Override public String toString() { - return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) + ",floatConvert[" + - toString(floatConvert) + "]]"; + return getClass().getSimpleName() + "[" + toString(neg, add, sub, mul, mulHigh, umulHigh, div, rem, not, and, or, xor, shl, shr, ushr, abs, sqrt, zeroExtend, signExtend, narrow) + + ",floatConvert[" + toString(floatConvert) + "]]"; } public abstract static class Op { @@ -479,6 +503,20 @@ } } + public abstract static class MulHigh extends BinaryOp { + + protected MulHigh(boolean associative, boolean commutative) { + super("*H", associative, commutative); + } + } + + public abstract static class UMulHigh extends BinaryOp { + + protected UMulHigh(boolean associative, boolean commutative) { + super("|*H|", associative, commutative); + } + } + public abstract static class Div extends BinaryOp
{ protected Div(boolean associative, boolean commutative) { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/FloatStamp.java Wed Aug 23 20:20:59 2017 +0000 @@ -302,7 +302,7 @@ return null; } - private static final ArithmeticOpTable OPS = new ArithmeticOpTable( + public static final ArithmeticOpTable OPS = new ArithmeticOpTable( new UnaryOp.Neg() { @@ -437,6 +437,10 @@ } }, + null, + + null, + new BinaryOp.Div(false, false) { @Override diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java Wed Aug 23 20:20:59 2017 +0000 @@ -858,6 +858,164 @@ } }, + new BinaryOp.MulHigh(true, true) { + + @Override + public Constant foldConstant(Constant const1, Constant const2) { + PrimitiveConstant a = (PrimitiveConstant) const1; + PrimitiveConstant b = (PrimitiveConstant) const2; + assert a.getJavaKind() == b.getJavaKind(); + return JavaConstant.forIntegerKind(a.getJavaKind(), multiplyHigh(a.asLong(), b.asLong(), a.getJavaKind())); + } + + @Override + public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { + IntegerStamp a = (IntegerStamp) stamp1; + IntegerStamp b = (IntegerStamp) stamp2; + JavaKind javaKind = a.getStackKind(); + + assert a.getBits() == b.getBits(); + assert javaKind == b.getStackKind(); + assert (javaKind == JavaKind.Int || javaKind == JavaKind.Long); + + if (a.isEmpty() || b.isEmpty()) { + return a.empty(); + } else if (a.isUnrestricted() || b.isUnrestricted()) { + return a.unrestricted(); + } + + long[] xExtremes = {a.lowerBound(), a.upperBound()}; + long[] yExtremes = {b.lowerBound(), b.upperBound()}; + long min = Long.MAX_VALUE; + long max = Long.MIN_VALUE; + for (long x : xExtremes) { + for (long y : yExtremes) { + long result = multiplyHigh(x, y, javaKind); + min = Math.min(min, result); + max = Math.max(max, result); + } + } + return StampFactory.forInteger(javaKind, min, max); + } + + @Override + public boolean isNeutral(Constant value) { + return false; + } + + private long multiplyHigh(long x, long y, JavaKind javaKind) { + if (javaKind == JavaKind.Int) { + return (x * y) >> 32; + } else { + assert javaKind == JavaKind.Long; + long x0 = x & 0xFFFFFFFFL; + long x1 = x >> 32; + + long y0 = y & 0xFFFFFFFFL; + long y1 = y >> 32; + + long z0 = x0 * y0; + long t = x1 * y0 + (z0 >>> 32); + long z1 = t & 0xFFFFFFFFL; + long z2 = t >> 32; + z1 += x0 * y1; + + return x1 * y1 + z2 + (z1 >> 32); + } + } + }, + + new BinaryOp.UMulHigh(true, true) { + + @Override + public Constant foldConstant(Constant const1, Constant const2) { + PrimitiveConstant a = (PrimitiveConstant) const1; + PrimitiveConstant b = (PrimitiveConstant) const2; + assert a.getJavaKind() == b.getJavaKind(); + return JavaConstant.forIntegerKind(a.getJavaKind(), multiplyHighUnsigned(a.asLong(), b.asLong(), a.getJavaKind())); + } + + @Override + public Stamp foldStamp(Stamp stamp1, Stamp stamp2) { + IntegerStamp a = (IntegerStamp) stamp1; + IntegerStamp b = (IntegerStamp) stamp2; + JavaKind javaKind = a.getStackKind(); + + assert a.getBits() == b.getBits(); + assert javaKind == b.getStackKind(); + assert (javaKind == JavaKind.Int || javaKind == JavaKind.Long); + + if (a.isEmpty() || b.isEmpty()) { + return a.empty(); + } else if (a.isUnrestricted() || b.isUnrestricted()) { + return a.unrestricted(); + } + + // Note that the minima and maxima are calculated using signed min/max + // functions, while the values themselves are unsigned. + long[] xExtremes = getUnsignedExtremes(a); + long[] yExtremes = getUnsignedExtremes(b); + long min = Long.MAX_VALUE; + long max = Long.MIN_VALUE; + for (long x : xExtremes) { + for (long y : yExtremes) { + long result = multiplyHighUnsigned(x, y, javaKind); + min = Math.min(min, result); + max = Math.max(max, result); + } + } + + // if min is negative, then the value can reach into the unsigned range + if (min == max || min >= 0) { + return StampFactory.forInteger(javaKind, min, max); + } else { + return StampFactory.forKind(javaKind); + } + } + + @Override + public boolean isNeutral(Constant value) { + return false; + } + + private long[] getUnsignedExtremes(IntegerStamp stamp) { + if (stamp.lowerBound() < 0 && stamp.upperBound() >= 0) { + /* + * If -1 and 0 are both in the signed range, then we can't say + * anything about the unsigned range, so we have to return [0, + * MAX_UNSIGNED]. + */ + return new long[]{0, -1L}; + } else { + return new long[]{stamp.lowerBound(), stamp.upperBound()}; + } + } + + private long multiplyHighUnsigned(long x, long y, JavaKind javaKind) { + if (javaKind == JavaKind.Int) { + long xl = x & 0xFFFFFFFFL; + long yl = y & 0xFFFFFFFFL; + long r = xl * yl; + return (int) (r >>> 32); + } else { + assert javaKind == JavaKind.Long; + long x0 = x & 0xFFFFFFFFL; + long x1 = x >>> 32; + + long y0 = y & 0xFFFFFFFFL; + long y1 = y >>> 32; + + long z0 = x0 * y0; + long t = x1 * y0 + (z0 >>> 32); + long z1 = t & 0xFFFFFFFFL; + long z2 = t >>> 32; + z1 += x0 * y1; + + return x1 * y1 + z2 + (z1 >>> 32); + } + } + }, + new BinaryOp.Div(true, false) { @Override @@ -1046,10 +1204,14 @@ public Stamp foldStamp(Stamp stamp, IntegerStamp shift) { IntegerStamp value = (IntegerStamp) stamp; int bits = value.getBits(); - long defaultMask = CodeUtil.mask(bits); - if (value.upMask() == 0) { + if (value.isEmpty()) { + return value; + } else if (shift.isEmpty()) { + return StampFactory.forInteger(bits).empty(); + } else if (value.upMask() == 0) { return value; } + int shiftMask = getShiftAmountMask(stamp); int shiftBits = Integer.bitCount(shiftMask); if (shift.lowerBound() == shift.upperBound()) { @@ -1068,6 +1230,7 @@ } } if ((shift.lowerBound() >>> shiftBits) == (shift.upperBound() >>> shiftBits)) { + long defaultMask = CodeUtil.mask(bits); long downMask = defaultMask; long upMask = 0; for (long i = shift.lowerBound(); i <= shift.upperBound(); i++) { @@ -1109,7 +1272,11 @@ public Stamp foldStamp(Stamp stamp, IntegerStamp shift) { IntegerStamp value = (IntegerStamp) stamp; int bits = value.getBits(); - if (shift.lowerBound() == shift.upperBound()) { + if (value.isEmpty()) { + return value; + } else if (shift.isEmpty()) { + return StampFactory.forInteger(bits).empty(); + } else if (shift.lowerBound() == shift.upperBound()) { long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp); if (shiftCount == 0) { return stamp; @@ -1153,6 +1320,12 @@ public Stamp foldStamp(Stamp stamp, IntegerStamp shift) { IntegerStamp value = (IntegerStamp) stamp; int bits = value.getBits(); + if (value.isEmpty()) { + return value; + } else if (shift.isEmpty()) { + return StampFactory.forInteger(bits).empty(); + } + if (shift.lowerBound() == shift.upperBound()) { long shiftCount = shift.lowerBound() & getShiftAmountMask(stamp); if (shiftCount == 0) { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeReadEliminationTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeReadEliminationTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeReadEliminationTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -46,6 +46,16 @@ public static double SideEffectD; public static double SideEffectL; + private static final long byteArrayBaseOffset; + private static final long intArrayBaseOffset; + private static final long longArrayBaseOffset; + + static { + byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class); + intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class); + longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class); + } + public static long test1Snippet(double a) { final Object m = Memory; if (a > 0) { @@ -130,4 +140,76 @@ Assert.assertEquals(writes, graph.getNodes().filter(WriteNode.class).count()); } + public static int testWriteIntToByteArraySnippet() { + byte[] array = new byte[4]; + UNSAFE.putInt(array, byteArrayBaseOffset, 0x01020304); + return array[0]; + } + + @Test + public void testWriteIntToByteArray() { + test("testWriteIntToByteArraySnippet"); + } + + public static byte testWriteSignedExtendedByteToByteArraySnippet(byte b) { + byte[] array = new byte[4]; + array[0] = 0x01; + array[1] = 0x02; + array[2] = 0x03; + array[3] = 0x04; + UNSAFE.putInt(array, byteArrayBaseOffset, b); + return array[3]; + } + + @Test + public void testWriteSignedExtendedByteToByteArray() { + test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0); + } + + public static int testWriteLongToIntArraySnippet() { + int[] array = new int[2]; + UNSAFE.putLong(array, intArrayBaseOffset, 0x0102030405060708L); + return array[0]; + } + + @Test + public void testWriteLongToIntArray() { + test("testWriteLongToIntArraySnippet"); + } + + public static int testWriteByteToIntArraySnippet() { + int[] array = new int[1]; + array[0] = 0x01020304; + UNSAFE.putByte(array, intArrayBaseOffset, (byte) 0x05); + return array[0]; + } + + @Test + public void testWriteByteToIntArray() { + test("testWriteByteToIntArraySnippet"); + } + + public static long testWriteIntToLongArraySnippet() { + long[] array = new long[1]; + array[0] = 0x0102030405060708L; + UNSAFE.putInt(array, longArrayBaseOffset, 0x04030201); + return array[0]; + } + + @Test + public void testWriteIntToLongArray() { + test("testWriteIntToLongArraySnippet"); + } + + public static float testWriteFloatToIntArraySnippet() { + float[] array = new float[1]; + UNSAFE.putInt(array, intArrayBaseOffset, Float.floatToRawIntBits(0.5f)); + return array[0]; + } + + @Test + public void testWriteFloatToIntArray() { + test("testWriteFloatToIntArraySnippet"); + } + } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java Wed Aug 23 20:20:59 2017 +0000 @@ -38,10 +38,6 @@ private static final long fieldOffset1; private static final long fieldOffset2; - private static final long byteArrayBaseOffset; - private static final long intArrayBaseOffset; - private static final long longArrayBaseOffset; - static { try { long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x")); @@ -55,9 +51,6 @@ fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z")); } assert fieldOffset2 == fieldOffset1 + 4; - byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class); - intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class); - longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class); } catch (Exception e) { throw new RuntimeException(e); } @@ -203,76 +196,4 @@ return x; } - public static int testWriteIntToByteArraySnippet() { - byte[] array = new byte[4]; - UNSAFE.putInt(array, byteArrayBaseOffset, 0x01020304); - return array[0]; - } - - @Test - public void testWriteIntToByteArray() { - test("testWriteIntToByteArraySnippet"); - } - - public static byte testWriteSignedExtendedByteToByteArraySnippet(byte b) { - byte[] array = new byte[4]; - array[0] = 0x01; - array[1] = 0x02; - array[2] = 0x03; - array[3] = 0x04; - UNSAFE.putInt(array, byteArrayBaseOffset, b); - return array[3]; - } - - @Test - public void testWriteSignedExtendedByteToByteArray() { - test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0); - } - - public static int testWriteLongToIntArraySnippet() { - int[] array = new int[2]; - UNSAFE.putLong(array, intArrayBaseOffset, 0x0102030405060708L); - return array[0]; - } - - @Test - public void testWriteLongToIntArray() { - test("testWriteLongToIntArraySnippet"); - } - - public static int testWriteByteToIntArraySnippet() { - int[] array = new int[1]; - array[0] = 0x01020304; - UNSAFE.putByte(array, intArrayBaseOffset, (byte) 0x05); - return array[0]; - } - - @Test - public void testWriteByteToIntArray() { - test("testWriteByteToIntArraySnippet"); - } - - public static long testWriteIntToLongArraySnippet() { - long[] array = new long[1]; - array[0] = 0x0102030405060708L; - UNSAFE.putInt(array, longArrayBaseOffset, 0x04030201); - return array[0]; - } - - @Test - public void testWriteIntToLongArray() { - test("testWriteIntToLongArraySnippet"); - } - - public static float testWriteFloatToIntArraySnippet() { - float[] array = new float[1]; - UNSAFE.putInt(array, intArrayBaseOffset, Float.floatToRawIntBits(0.5f)); - return array[0]; - } - - @Test - public void testWriteFloatToIntArray() { - test("testWriteFloatToIntArraySnippet"); - } - } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilationPrinter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilationPrinter.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilationPrinter.java Wed Aug 23 20:20:59 2017 +0000 @@ -58,6 +58,11 @@ */ public static CompilationPrinter begin(OptionValues options, CompilationIdentifier id, JavaMethod method, int entryBCI) { if (PrintCompilation.getValue(options) && !TTY.isSuppressed()) { + try { + Class.forName("java.lang.management.ManagementFactory"); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException("PrintCompilation option requires java.management module"); + } return new CompilationPrinter(id, method, entryBCI); } return DISABLED; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilationWrapper.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilationWrapper.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/CompilationWrapper.java Wed Aug 23 20:20:59 2017 +0000 @@ -82,26 +82,6 @@ */ ExitVM; - static ValueHelp HELP = new ValueHelp(); - - static class ValueHelp implements EnumOptionKey.ValueHelp { - @Override - public String getHelp(Object value) { - ExceptionAction action = (ExceptionAction) value; - switch (action) { - case Silent: - return action + ": Print nothing to the console."; - case Print: - return action + ": Print a stack trace to the console."; - case Diagnose: - return action + ": Retry the compilation with extra diagnostics."; - case ExitVM: - return action + ": Same as " + Diagnose + " except that the VM process exits after retrying."; - } - return null; - } - } - /** * Gets the action that is one level less verbose than this action, bottoming out at the * least verbose action. diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompilerOptions.java Wed Aug 23 20:20:59 2017 +0000 @@ -36,13 +36,15 @@ // @formatter:off @Option(help = "Print an informational line to the console for each completed compilation.", type = OptionType.Debug) public static final OptionKey PrintCompilation = new OptionKey<>(false); - @Option(help = "Pattern (see MethodFilter for format) for method that will trigger an exception when compiled. " + - "This option exists to test handling compilation crashes gracefully.", type = OptionType.Debug) + @Option(help = "Pattern for method(s) that will trigger an exception when compiled. " + + "This option exists to test handling compilation crashes gracefully. " + + "See the MethodFilter option for the pattern syntax. ", type = OptionType.Debug) public static final OptionKey CrashAt = new OptionKey<>(null); - @Option(help = "The action to take when compilation fails with a non-bailout exception.", type = OptionType.User) - public static final EnumOptionKey CompilationFailureAction = new EnumOptionKey<>(ExceptionAction.Diagnose, ExceptionAction.HELP); - @Option(help = "The action to take when compilation fails with a bailout exception.", type = OptionType.User) - public static final EnumOptionKey CompilationBailoutAction = new EnumOptionKey<>(ExceptionAction.Silent, ExceptionAction.HELP); + @Option(help = "file:doc-files/CompilationBailoutActionHelp.txt", type = OptionType.User) + public static final EnumOptionKey CompilationBailoutAction = new EnumOptionKey<>(ExceptionAction.Silent); + @Option(help = "Specifies the action to take when compilation fails with a bailout exception. " + + "The accepted values are the same as for CompilationBailoutAction.", type = OptionType.User) + public static final EnumOptionKey CompilationFailureAction = new EnumOptionKey<>(ExceptionAction.Diagnose); @Option(help = "The maximum number of compilation failures or bailouts to handle with the action specified " + "by CompilationFailureAction or CompilationBailoutAction before changing to a less verbose action.", type = OptionType.User) public static final OptionKey MaxCompilationProblemsPerAction = new OptionKey<>(5); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/doc-files/CompilationBailoutActionHelp.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/doc-files/CompilationBailoutActionHelp.txt Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,6 @@ +Specifies the action to take when compilation fails with a bailout exception. +The accepted values are: + Silent - Print nothing to the console. + Print - Print a stack trace to the console. + Diagnose - Retry the compilation with extra diagnostics. + ExitVM - Same as Diagnose except that the VM process exits after retrying. \ No newline at end of file diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java Wed Aug 23 20:20:59 2017 +0000 @@ -198,9 +198,22 @@ private Immutable(OptionValues options) { this.options = options; + String timeValue = Time.getValue(options); + String trackMemUseValue = TrackMemUse.getValue(options); this.unscopedCounters = parseUnscopedMetricSpec(Counters.getValue(options), "".equals(Count.getValue(options)), false); - this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(Time.getValue(options)), true); - this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(TrackMemUse.getValue(options)), true); + this.unscopedTimers = parseUnscopedMetricSpec(Timers.getValue(options), "".equals(timeValue), true); + this.unscopedMemUseTrackers = parseUnscopedMetricSpec(MemUseTrackers.getValue(options), "".equals(trackMemUseValue), true); + + if (unscopedTimers != null || + unscopedMemUseTrackers != null || + timeValue != null || + trackMemUseValue != null) { + try { + Class.forName("java.lang.management.ManagementFactory"); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException("Time, Timers, MemUseTrackers and TrackMemUse options require java.management module"); + } + } this.scopesEnabled = DumpOnError.getValue(options) || Dump.getValue(options) != null || diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugFilter.java Wed Aug 23 20:20:59 2017 +0000 @@ -28,8 +28,11 @@ import org.graalvm.compiler.debug.DebugContext.Scope; /** - * Implements the filter specified by the {@link DebugOptions#Dump}, {@link DebugOptions#Log}, - * {@link DebugOptions#Count} and {@link DebugOptions#Time} options. + * Implements the filter specified by options such as {@link DebugOptions#Dump}, + * {@link DebugOptions#Log}, {@link DebugOptions#Count} and {@link DebugOptions#Time}. + * + * See here for a description of the filter syntax. + * *

* These options enable the associated debug facility if their filter matches the * {@linkplain Scope#getQualifiedName() name} of the current scope. For the @@ -37,47 +40,7 @@ * {@link DebugOptions#Count} and {@link DebugOptions#Time} options don't have a level, for them * {@code level = 0} means disabled and a {@code level > 0} means enabled. *

- * A filter is a list of comma-separated terms of the form {@code [:]}. {@code - * } is interpreted as a glob pattern if it contains a "*" or "?" character. Otherwise, it - * is interpreted as a substring. If {@code } is empty, it matches every scope. If {@code : - * } is omitted, it defaults to {@link DebugContext#BASIC_LEVEL}. The term {@code ~} - * is a shorthand for {@code :0} to disable a debug facility for a pattern. - *

- * The resulting log level of a scope is determined by the last matching term. If no term - * matches, the log level is 0 (disabled). A filter with no terms matches every scope with a log - * level of {@link DebugContext#BASIC_LEVEL}. - * - *

Examples of filters

- * - *
    - *
  • (empty string)
    - * Matches any scope with log level {@link DebugContext#BASIC_LEVEL}. - * - *
  • {@code :1}
    - * Matches any scope with log level 1. - * - *
  • {@code *}
    - * Matches any scope with log level {@link DebugContext#BASIC_LEVEL}. - * - *
  • {@code CodeGen,CodeInstall}
    - * Matches scopes containing "CodeGen" or "CodeInstall", both with log level - * {@link DebugContext#BASIC_LEVEL}. - * - *
  • {@code CodeGen:2,CodeInstall:1}
    - * Matches scopes containing "CodeGen" with log level 2, or "CodeInstall" with log level 1. - * - *
  • {@code :1,Dead:2}
    - * Matches scopes containing "Dead" with log level 2, and all other scopes with log level 1. - * - *
  • {@code :1,Dead:0}
    - * Matches all scopes with log level 1, except those containing "Dead". - * - *
  • {@code Code*}
    - * Matches scopes starting with "Code" with log level {@link DebugContext#BASIC_LEVEL}. - * - *
  • {@code Code,~Dead}
    - * Matches scopes containing "Code" but not "Dead", with log level {@link DebugContext#BASIC_LEVEL}. - *
+ * The syntax for a filter is explained here. */ final class DebugFilter { @@ -148,13 +111,16 @@ if (terms == null) { return DebugContext.BASIC_LEVEL; } else { - int level = 0; + int defaultLevel = 0; + int level = -1; for (Term t : terms) { - if (t.matches(input)) { + if (t.isMatchAny()) { + defaultLevel = t.level; + } else if (t.matches(input)) { level = t.level; } } - return level; + return level == -1 ? defaultLevel : level; } } @@ -176,7 +142,7 @@ Term(String filter, int level) { this.level = level; - if (filter.isEmpty()) { + if (filter.isEmpty() || filter.equals("*")) { this.pattern = null; } else if (filter.contains("*") || filter.contains("?")) { this.pattern = Pattern.compile(MethodFilter.createGlobString(filter)); @@ -192,6 +158,10 @@ return pattern == null || pattern.matcher(input).matches(); } + public boolean isMatchAny() { + return pattern == null; + } + @Override public String toString() { return (pattern == null ? ".*" : pattern.toString()) + ":" + level; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java Wed Aug 23 20:20:59 2017 +0000 @@ -64,24 +64,28 @@ "An empty value enables all memory usage trackers unconditionally.", type = OptionType.Debug) public static final OptionKey MemUseTrackers = new OptionKey<>(null); - @Option(help = "Pattern for scope(s) in which counting is enabled (see DebugFilter and Debug.counter). " + + @Option(help = "Pattern for specifying scopes in which counters are enabled. " + + "See the Dump option for the pattern syntax. " + "An empty value enables all counters unconditionally.", type = OptionType.Debug) public static final OptionKey Count = new OptionKey<>(null); - @Option(help = "Pattern for scope(s) in which memory use tracking is enabled (see DebugFilter and Debug.counter). " + + @Option(help = "Pattern for specifying scopes in which memory use tracking is enabled. " + + "See the Dump option for the pattern syntax. " + "An empty value enables all memory use trackers unconditionally.", type = OptionType.Debug) public static final OptionKey TrackMemUse = new OptionKey<>(null); - @Option(help = "Pattern for scope(s) in which timing is enabled (see DebugFilter and Debug.timer). " + + @Option(help = "Pattern for specifying scopes in which timing is enabled. " + + "See the Dump option for the pattern syntax. " + "An empty value enables all timers unconditionally.", type = OptionType.Debug) public static final OptionKey Time = new OptionKey<>(null); - @Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug) + @Option(help = "Pattern for specifying scopes in which logging is enabled. " + + "See the Dump option for the pattern syntax.", type = OptionType.Debug) public static final OptionKey Verify = new OptionKey<>(null); - @Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug) + @Option(help = "file:doc-files/DumpHelp.txt", type = OptionType.Debug) public static final OptionKey Dump = new OptionKey<>(null); - @Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug) + @Option(help = "Pattern for specifying scopes in which logging is enabled. " + + "See the Dump option for the pattern syntax.", type = OptionType.Debug) public static final OptionKey Log = new OptionKey<>(null); - - @Option(help = "Pattern for filtering debug scope output based on method context (see MethodFilter)", type = OptionType.Debug) + @Option(help = "file:doc-files/MethodFilterHelp.txt") public static final OptionKey MethodFilter = new OptionKey<>(null); @Option(help = "Only check MethodFilter against the root method in the context if true, otherwise check all methods", type = OptionType.Debug) public static final OptionKey MethodFilterRootOnly = new OptionKey<>(false); @@ -89,13 +93,11 @@ "The argument is substring matched against the simple name of the phase class", type = OptionType.Debug) public static final OptionKey DumpOnPhaseChange = new OptionKey<>(null); - @Option(help = "Listst the console at VM shutdown the metric names available to the Timers, Counters and MemUseTrackers option. " + + @Option(help = "Lists on the console at VM shutdown the metric names available to the Timers, Counters and MemUseTrackers options. " + "Note that this only lists the metrics that were initialized during the VM execution and so " + "will not include metrics for compiler code that is not executed.", type = OptionType.Debug) public static final OptionKey ListMetrics = new OptionKey<>(false); - @Option(help = "File to which metrics are dumped per compilation. A CSV format is used if the file ends with .csv " + - "otherwise a more human readable format is used. The fields in the CSV format are: " + - "compilable, compilable_identity, compilation_nr, compilation_id, metric_name, metric_value", type = OptionType.Debug) + @Option(help = "file:doc-files/MetricsFileHelp.txt", type = OptionType.Debug) public static final OptionKey MetricsFile = new OptionKey<>(null); @Option(help = "File to which aggregated metrics are dumped at shutdown. A CSV format is used if the file ends with .csv " + "otherwise a more human readable format is used. If not specified, metrics are dumped to the console.", type = OptionType.Debug) @@ -149,7 +151,7 @@ @Option(help = "Enable dumping canonical text from for graphs.", type = OptionType.Debug) public static final OptionKey PrintCanonicalGraphStrings = new OptionKey<>(false); @Option(help = "Choose format used when dumping canonical text for graphs: " + - "0 gives a scheduled graph (better for spotting changes involving the schedule)" + + "0 gives a scheduled graph (better for spotting changes involving the schedule) " + "while 1 gives a CFG containing expressions rooted at fixed nodes (better for spotting small structure differences)", type = OptionType.Debug) public static final OptionKey PrintCanonicalGraphStringFlavor = new OptionKey<>(0); @Option(help = "Exclude virtual nodes when dumping canonical text for graphs.", type = OptionType.Debug) diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/MethodFilter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/MethodFilter.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/MethodFilter.java Wed Aug 23 20:20:59 2017 +0000 @@ -31,66 +31,7 @@ /** * This class implements a method filter that can filter based on class name, method name and - * parameters. The syntax for the source pattern that is passed to the constructor is as follows: - * - *
- * SourcePatterns = SourcePattern ["," SourcePatterns] .
- * SourcePattern = [ Class "." ] method [ "(" [ Parameter { ";" Parameter } ] ")" ] .
- * Parameter = Class | "int" | "long" | "float" | "double" | "short" | "char" | "boolean" .
- * Class = { package "." } class .
- * 
- * - * - * Glob pattern matching (*, ?) is allowed in all parts of the source pattern. Examples for valid - * filters are: - * - *
    - *
  • - * - *
    - * visit(Argument;BlockScope)
    - * 
    - * - * Matches all methods named "visit", with the first parameter of type "Argument", and the second - * parameter of type "BlockScope". The packages of the parameter types are irrelevant.
  • - *
  • - * - *
    - * arraycopy(Object;;;;)
    - * 
    - * - * Matches all methods named "arraycopy", with the first parameter of type "Object", and four more - * parameters of any type. The packages of the parameter types are irrelevant.
  • - *
  • - * - *
    - * org.graalvm.compiler.core.graph.PostOrderNodeIterator.*
    - * 
    - * - * Matches all methods in the class "org.graalvm.compiler.core.graph.PostOrderNodeIterator".
  • - *
  • - * - *
    - * *
    - * 
    - * - * Matches all methods in all classes
  • - *
  • - * - *
    - * org.graalvm.compiler.core.graph.*.visit
    - * 
    - * - * Matches all methods named "visit" in classes in the package "org.graalvm.compiler.core.graph". - *
  • - * - *
    - * arraycopy,toString
    - * 
    - * - * Matches all methods named "arraycopy" or "toString", meaning that ',' acts as an or - * operator.
  • - *
+ * parameters. The syntax for a filter is explained here. */ public class MethodFilter { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/doc-files/DumpHelp.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/doc-files/DumpHelp.txt Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,61 @@ +Filter pattern for specifying scopes in which dumping is enabled. + +A filter is a list of comma-separated terms of the form: + + [:] + +If contains a "*" or "?" character, it is interpreted as a glob pattern. +Otherwise, it is interpreted as a substring. If is empty, it +matches every scope. If : is omitted, it defaults to 1. The term +~ is a shorthand for :0 to disable a debug facility for a pattern. + +The default log level is 0 (disabled). Terms with an empty pattern set +the default log level to the specified value. The last +matching term with a non-empty pattern selects the level specified. If +no term matches, the log level is the default level. A filter with no +terms matches every scope with a log level of 1. + +Examples of debug filters: +--------- + (empty string) + + Matches any scope with level 1. +--------- + :1 + + Matches any scope with level 1. +--------- + * + + Matches any scope with level 1. +--------- + CodeGen,CodeInstall + + Matches scopes containing "CodeGen" or "CodeInstall", both with level 1. +--------- + CodeGen:2,CodeInstall:1 + + Matches scopes containing "CodeGen" with level 2, or "CodeInstall" with level 1. +--------- + Outer:2,Inner:0} + + Matches scopes containing "Outer" with log level 2, or "Inner" with log level 0. If the scope + name contains both patterns then the log level will be 0. This is useful for silencing subscopes. +--------- + :1,Dead:2 + + Matches scopes containing "Dead" with level 2, and all other scopes with level 1. +--------- + Dead:0,:1 + + Matches all scopes with level 1, except those containing "Dead". Note that the location of + the :1 doesn't matter since it's specifying the default log level so it's the same as + specifying :1,Dead:0. +--------- + Code* + + Matches scopes starting with "Code" with level 1. +--------- + Code,~Dead + + Matches scopes containing "Code" but not "Dead", with level 1. diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/doc-files/MethodFilterHelp.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/doc-files/MethodFilterHelp.txt Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,40 @@ +Pattern for filtering debug scope output based on method context. +The syntax for a pattern is: + + SourcePatterns = SourcePattern ["," SourcePatterns] . + SourcePattern = [ Class "." ] method [ "(" [ Parameter { ";" Parameter } ] ")" ] . + Parameter = Class | "int" | "long" | "float" | "double" | "short" | "char" | "boolean" . + Class = { package "." } class . + +Glob pattern matching (*, ?) is allowed in all parts of the source pattern. + +Examples of method filters: +--------- + visit(Argument;BlockScope) + + Matches all methods named "visit", with the first parameter of + type "Argument", and the second parameter of type "BlockScope". + The packages of the parameter types are irrelevant. +--------- + arraycopy(Object;;;;) + + Matches all methods named "arraycopy", with the first parameter + of type "Object", and four more parameters of any type. The + packages of the parameter types are irrelevant. +--------- + org.graalvm.compiler.core.graph.PostOrderNodeIterator.* + + Matches all methods in the class "org.graalvm.compiler.core.graph.PostOrderNodeIterator". +--------- + * + + Matches all methods in all classes +--------- + org.graalvm.compiler.core.graph.*.visit + + Matches all methods named "visit" in classes in the package + "org.graalvm.compiler.core.graph". +--------- + arraycopy,toString + + Matches all methods named "arraycopy" or "toString", meaning that ',' acts as an or operator. diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/doc-files/MetricsFileHelp.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/doc-files/MetricsFileHelp.txt Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,11 @@ +File to which metrics are dumped per compilation. +A CSV format is used if the file ends with .csv otherwise a more +human readable format is used. The fields in the CSV format are: + compilable - method being compiled + compilable_identity - identity hash code of compilable + compilation_nr - where this compilation lies in the ordered + sequence of all compilations identified by + compilable_identity + compilation_id - runtime issued identifier for the compilation + metric_name - name of metric + metric_value - value of metric diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java Wed Aug 23 20:20:59 2017 +0000 @@ -931,7 +931,7 @@ } /** - * @returns true if the node has no inputs and no successors + * @return true if the node has no inputs and no successors */ public boolean isLeafNode() { return isLeafNode; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -40,20 +40,12 @@ import org.graalvm.compiler.test.SubprocessUtil; import org.graalvm.compiler.test.SubprocessUtil.Subprocess; import org.junit.Assert; -import org.junit.Assume; import org.junit.Test; /** * Tests support for dumping graphs and other info useful for debugging a compiler crash. */ public class CompilationWrapperTest extends GraalCompilerTest { - public CompilationWrapperTest() { - try { - Class.forName("java.lang.management.ManagementFactory"); - } catch (ClassNotFoundException ex) { - Assume.assumeNoException("skip this test if there is no java.management JDK9 module around", ex); - } - } /** * Tests compilation requested by the VM. diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ObjectCloneTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ObjectCloneTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ObjectCloneTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,6 +25,7 @@ import java.util.ArrayList; import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import org.junit.Test; /** @@ -84,4 +85,20 @@ } test("cloneList", list); } + + @Override + protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) { + return super.editGraphBuilderConfiguration(conf.withNodeSourcePosition(true)); + } + + static final int[] ARRAY = new int[]{1, 2, 4, 3}; + + public static int[] cloneConstantArray() { + return ARRAY.clone(); + } + + @Test + public void testCloneConstantArray() { + test("cloneConstantArray"); + } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java Wed Aug 23 20:20:59 2017 +0000 @@ -157,8 +157,8 @@ /** * Selects and instantiates a {@link CompilerConfigurationFactory}. The selection algorithm is * as follows: if {@code name} is non-null, then select the factory with the same name else if - * {@link Options#CompilerConfiguration}{@code .getValue()} is non-null then select the factory - * whose name matches the value else select the factory with the highest + * {@code Options.CompilerConfiguration.getValue()} is non-null then select the factory whose + * name matches the value else select the factory with the highest * {@link #autoSelectionPriority} value. * * @param name the name of the compiler configuration to select (optional) diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java Wed Aug 23 20:20:59 2017 +0000 @@ -23,7 +23,6 @@ package org.graalvm.compiler.hotspot; import java.util.Formatter; -import java.util.Objects; /** * Mechanism for checking that the current Java runtime environment supports the minimum JVMCI API @@ -40,10 +39,6 @@ private static final int JVMCI8_MIN_MAJOR_VERSION = 0; private static final int JVMCI8_MIN_MINOR_VERSION = 29; - // MAX_VALUE indicates that no current EA version is compatible with Graal. - // Note: Keep README.md in sync with the EA version support checked here. - private static final int JVMCI9_MIN_EA_BUILD = 176; - private static void failVersionCheck(boolean exit, String reason, Object... args) { Formatter errorMessage = new Formatter().format(reason, args); String javaHome = System.getProperty("java.home"); @@ -55,7 +50,7 @@ if (System.getProperty("java.specification.version").compareTo("1.9") < 0) { errorMessage.format("Download the latest JVMCI JDK 8 from http://www.oracle.com/technetwork/oracle-labs/program-languages/downloads/index.html"); } else { - errorMessage.format("Download the latest JDK 9 EA from https://jdk9.java.net/download/"); + errorMessage.format("Download the latest JDK 9 build from https://jdk9.java.net/download/"); } String value = System.getenv("JVMCI_VERSION_CHECK"); if ("warn".equals(value)) { @@ -119,34 +114,11 @@ // Allow local builds return; } - // http://openjdk.java.net/jeps/223 - if (vmVersion.startsWith("9+")) { - int start = "9+".length(); - int end = start; - end = start; - while (end < vmVersion.length() && Character.isDigit(vmVersion.charAt(end))) { - end++; - } - int build; - try { - build = Integer.parseInt(vmVersion.substring(start, end)); - } catch (NumberFormatException e) { - failVersionCheck(exitOnFailure, "The VM does not support the minimum JVMCI API version required by Graal.%n" + - "Cannot read JDK9 EA build number from java.vm.version property: %s.%n", vmVersion); - return; - } - if (build >= JVMCI9_MIN_EA_BUILD) { - return; - } - if (Objects.equals(JVMCI9_MIN_EA_BUILD, Integer.MAX_VALUE)) { - failVersionCheck(exitOnFailure, "This version of Graal is not compatible with any JDK 9 Early Access build.%n"); - } else { - failVersionCheck(exitOnFailure, "The VM is an insufficiently recent EA JDK9 build for Graal: %d < %d.%n", build, JVMCI9_MIN_EA_BUILD); - } + if (vmVersion.startsWith("9-ea")) { + failVersionCheck(exitOnFailure, "This version of Graal is not compatible with JDK 9 Early Access builds.%n"); return; } else { - // Graal will be compatible with all JDK versions as of 9 GA - // until a JVMCI API change is made in a 9u or later release. + // Graal is compatible with all JDK versions as of 9 GA. } } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/BenchmarkCounters.java Wed Aug 23 20:20:59 2017 +0000 @@ -41,7 +41,6 @@ import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; -import org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions; import org.graalvm.compiler.nodes.debug.DynamicCounterNode; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; @@ -69,20 +68,8 @@ * Counters will be displayed as a rate (per second) if their group name starts with "~", otherwise * they will be displayed as a total number. * - *

Example

In order to create statistics about allocations within the DaCapo pmd benchmark - * the following steps are necessary: - *
    - *
  • Set {@code -XX:JVMCICounterSize=value}. The actual required value depends on the granularity - * of the profiling, 10000 should be enough for most cases.
  • - *
  • Also: {@code -XX:+/-JVMCICountersExcludeCompiler} specifies whether the numbers generated by - * compiler threads should be excluded (default: true).
  • - *
  • Start the DaCapo pmd benchmark with - * {@code "-Dgraal.BenchmarkDynamicCounters=err, starting ====, PASSED in "} and - * {@code -Dgraal.ProfileAllocations=true}.
  • - *
  • The numbers will only include allocation from compiled code!
  • - *
  • The counters can be further configured by modifying the - * {@link HotspotSnippetsOptions#ProfileAllocationsContext} flag..
  • - *
+ * See here for a detailed example of how to use + * benchmark counters. */ public class BenchmarkCounters { @@ -94,11 +81,7 @@ @Option(help = "Turn on the benchmark counters, and displays the results every n milliseconds", type = OptionType.Debug) public static final OptionKey TimedDynamicCounters = new OptionKey<>(-1); - @Option(help = "Turn on the benchmark counters, and listen for specific patterns on System.out/System.err:%n" + - "Format: (err|out),start pattern,end pattern (~ matches multiple digits)%n" + - "Examples:%n" + - " dacapo = 'err, starting =====, PASSED in'%n" + - " specjvm2008 = 'out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends:'", type = OptionType.Debug) + @Option(help = "file:doc-files/BenchmarkDynamicCountersHelp.txt", type = OptionType.Debug) public static final OptionKey BenchmarkDynamicCounters = new OptionKey<>(null); @Option(help = "Use grouping separators for number printing", type = OptionType.Debug) public static final OptionKey DynamicCountersPrintGroupSeparator = new OptionKey<>(true); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/doc-files/BenchmarkDynamicCountersHelp.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/debug/doc-files/BenchmarkDynamicCountersHelp.txt Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,24 @@ +Turn on the benchmark counters, and listen for specific patterns on System.out/System.err. +The format of this option is: + + (err|out),start pattern,end pattern + +You can use "~" to match 1 or more digits. +Examples: + + err, starting =====, PASSED in + out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends: + +The first pattern matches DaCapo output and the second matches SPECjvm2008 output. + +As a more detailed example, here are the options to use for getting statistics +about allocations within the DaCapo pmd benchmark: + + -XX:JVMCICounterSize= -XX:-JVMCICountersExcludeCompiler \ + -Dgraal.BenchmarkDynamicCounters="err, starting ====, PASSED in " \ + -Dgraal.ProfileAllocations=true + +The JVMCICounterSize value depends on the granularity of the profiling - +10000 should be sufficient. Omit JVMCICountersExcludeCompiler to exclude +counting allocations on the compiler threads. +The counters can be further configured by the ProfileAllocationsContext option. \ No newline at end of file diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,6 +25,7 @@ import static org.graalvm.compiler.core.common.GraalOptions.GeneratePIC; import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode; import static org.graalvm.compiler.core.common.GraalOptions.VerifyPhases; +import static org.graalvm.compiler.core.phases.HighTier.Options.Inline; import java.util.ListIterator; @@ -98,10 +99,12 @@ midTierLowering.add(new ReplaceConstantNodesPhase()); // Replace inlining policy - ListIterator> iter = ret.getHighTier().findPhase(InliningPhase.class); - InliningPhase inlining = (InliningPhase) iter.previous(); - CanonicalizerPhase canonicalizer = inlining.getCanonicalizer(); - iter.set(new InliningPhase(new AOTInliningPolicy(null), canonicalizer)); + if (Inline.getValue(options)) { + ListIterator> iter = ret.getHighTier().findPhase(InliningPhase.class); + InliningPhase inlining = (InliningPhase) iter.previous(); + CanonicalizerPhase canonicalizer = inlining.getCanonicalizer(); + iter.set(new InliningPhase(new AOTInliningPolicy(null), canonicalizer)); + } } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotspotSnippetsOptions.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotspotSnippetsOptions.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotspotSnippetsOptions.java Wed Aug 23 20:20:59 2017 +0000 @@ -51,7 +51,7 @@ @Option(help = "Enable profiling of allocation sites.", type = OptionType.Debug) public static final OptionKey ProfileAllocations = new OptionKey<>(false); - @Option(help = "Control the naming of the counters when using ProfileAllocations.", type = OptionType.Debug) + @Option(help = "file:doc-files/ProfileAllocationsContextHelp.txt", type = OptionType.Debug) public static final EnumOptionKey ProfileAllocationsContext = new EnumOptionKey<>(ProfileContext.AllocatingMethod); @Option(help = "Enable profiling of monitor operations.", type = OptionType.Debug) diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/doc-files/ProfileAllocationsContextHelp.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/doc-files/ProfileAllocationsContextHelp.txt Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,8 @@ +Control the naming and granularity of the counters when using ProfileAllocations. +The accepted values are: + AllocatingMethod - a counter per method + InstanceOrArray - one counter for all instance allocations and + one counter for all array allocations + AllocatedType - one counter per allocated type + AllocatedTypesInMethod - one counter per allocated type, per method + \ No newline at end of file diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/ComputeLoopFrequenciesClosure.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/ComputeLoopFrequenciesClosure.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/ComputeLoopFrequenciesClosure.java Wed Aug 23 20:20:59 2017 +0000 @@ -36,6 +36,8 @@ import org.graalvm.compiler.phases.graph.ReentrantNodeIterator; import org.graalvm.util.EconomicMap; +import static org.graalvm.compiler.nodes.cfg.ControlFlowGraph.multiplyProbabilities; + public final class ComputeLoopFrequenciesClosure extends ReentrantNodeIterator.NodeIteratorClosure { private static final ComputeLoopFrequenciesClosure INSTANCE = new ComputeLoopFrequenciesClosure(); @@ -75,32 +77,18 @@ for (double d : exitStates.getValues()) { exitProbability += d; } - exitProbability = Math.min(1D, exitProbability); - if (exitProbability < ControlFlowGraph.MIN_PROBABILITY) { - exitProbability = ControlFlowGraph.MIN_PROBABILITY; - } - assert exitProbability <= 1D && exitProbability >= 0D; - double loopFrequency = 1D / exitProbability; + exitProbability = Math.min(1.0, exitProbability); + exitProbability = Math.max(ControlFlowGraph.MIN_PROBABILITY, exitProbability); + double loopFrequency = 1.0 / exitProbability; loop.setLoopFrequency(loopFrequency); double adjustmentFactor = initialState * loopFrequency; - exitStates.replaceAll((exitNode, probability) -> multiplySaturate(probability, adjustmentFactor)); + exitStates.replaceAll((exitNode, probability) -> multiplyProbabilities(probability, adjustmentFactor)); return exitStates; } /** - * Multiplies a and b and saturates the result to {@link ControlFlowGraph#MAX_PROBABILITY}. - */ - public static double multiplySaturate(double a, double b) { - double r = a * b; - if (r > ControlFlowGraph.MAX_PROBABILITY) { - return ControlFlowGraph.MAX_PROBABILITY; - } - return r; - } - - /** * Computes the frequencies of all loops in the given graph. This is done by performing a * reverse postorder iteration and computing the probability of all fixed nodes. The combined * probability of all exits of a loop can be used to compute the loop's expected frequency. diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArrayEqualsOp.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArrayEqualsOp.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArrayEqualsOp.java Wed Aug 23 20:20:59 2017 +0000 @@ -68,6 +68,8 @@ public AArch64ArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, Value result, Value array1, Value array2, Value length) { super(TYPE); + + assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported"; this.kind = kind; Class arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass(); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64ArrayEqualsOp.java Wed Aug 23 20:20:59 2017 +0000 @@ -33,6 +33,8 @@ import org.graalvm.compiler.asm.amd64.AMD64Address; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag; +import org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize; +import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp; import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.lir.LIRInstructionClass; @@ -69,6 +71,10 @@ @Temp({REG}) protected Value temp2; @Temp({REG}) protected Value temp3; @Temp({REG}) protected Value temp4; + + @Temp({REG, ILLEGAL}) protected Value temp5; + @Temp({REG, ILLEGAL}) protected Value tempXMM; + @Temp({REG, ILLEGAL}) protected Value vectorTemp1; @Temp({REG, ILLEGAL}) protected Value vectorTemp2; @@ -91,6 +97,15 @@ this.temp3 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())); this.temp4 = tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())); + this.temp5 = kind.isNumericFloat() ? tool.newVariable(LIRKind.value(tool.target().arch.getWordKind())) : Value.ILLEGAL; + if (kind == JavaKind.Float) { + this.tempXMM = tool.newVariable(LIRKind.value(AMD64Kind.SINGLE)); + } else if (kind == JavaKind.Double) { + this.tempXMM = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE)); + } else { + this.tempXMM = Value.ILLEGAL; + } + // We only need the vector temporaries if we generate SSE code. if (supportsSSE41(tool.target())) { this.vectorTemp1 = tool.newVariable(LIRKind.value(AMD64Kind.DOUBLE)); @@ -170,10 +185,14 @@ Label loop = new Label(); Label compareTail = new Label(); + boolean requiresNaNCheck = kind.isNumericFloat(); + Label loopCheck = new Label(); + Label nanCheck = new Label(); + // Compare 16-byte vectors masm.andl(result, SSE4_1_VECTOR_SIZE - 1); // tail count (in bytes) masm.andl(length, ~(SSE4_1_VECTOR_SIZE - 1)); // vector count (in bytes) - masm.jccb(ConditionFlag.Zero, compareTail); + masm.jcc(ConditionFlag.Zero, compareTail); masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0)); masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0)); @@ -186,13 +205,24 @@ masm.movdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0)); masm.pxor(vector1, vector2); masm.ptest(vector1, vector1); - masm.jcc(ConditionFlag.NotZero, falseLabel); + masm.jcc(ConditionFlag.NotZero, requiresNaNCheck ? nanCheck : falseLabel); + + masm.bind(loopCheck); masm.addq(length, SSE4_1_VECTOR_SIZE); masm.jcc(ConditionFlag.NotZero, loop); masm.testl(result, result); masm.jcc(ConditionFlag.Zero, trueLabel); + if (requiresNaNCheck) { + Label unalignedCheck = new Label(); + masm.jmpb(unalignedCheck); + masm.bind(nanCheck); + emitFloatCompareWithinRange(crb, masm, array1, array2, length, 0, falseLabel, SSE4_1_VECTOR_SIZE); + masm.jmpb(loopCheck); + masm.bind(unalignedCheck); + } + /* * Compare the remaining bytes with an unaligned memory load aligned to the end of the * array. @@ -201,7 +231,12 @@ masm.movdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -SSE4_1_VECTOR_SIZE)); masm.pxor(vector1, vector2); masm.ptest(vector1, vector1); - masm.jcc(ConditionFlag.NotZero, falseLabel); + if (requiresNaNCheck) { + masm.jcc(ConditionFlag.Zero, trueLabel); + emitFloatCompareWithinRange(crb, masm, array1, array2, result, -SSE4_1_VECTOR_SIZE, falseLabel, SSE4_1_VECTOR_SIZE); + } else { + masm.jcc(ConditionFlag.NotZero, falseLabel); + } masm.jmp(trueLabel); masm.bind(compareTail); @@ -233,10 +268,14 @@ Label loop = new Label(); Label compareTail = new Label(); + boolean requiresNaNCheck = kind.isNumericFloat(); + Label loopCheck = new Label(); + Label nanCheck = new Label(); + // Compare 16-byte vectors masm.andl(result, AVX_VECTOR_SIZE - 1); // tail count (in bytes) masm.andl(length, ~(AVX_VECTOR_SIZE - 1)); // vector count (in bytes) - masm.jccb(ConditionFlag.Zero, compareTail); + masm.jcc(ConditionFlag.Zero, compareTail); masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0)); masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0)); @@ -249,13 +288,24 @@ masm.vmovdqu(vector2, new AMD64Address(array2, length, Scale.Times1, 0)); masm.vpxor(vector1, vector1, vector2); masm.vptest(vector1, vector1); - masm.jcc(ConditionFlag.NotZero, falseLabel); + masm.jcc(ConditionFlag.NotZero, requiresNaNCheck ? nanCheck : falseLabel); + + masm.bind(loopCheck); masm.addq(length, AVX_VECTOR_SIZE); masm.jcc(ConditionFlag.NotZero, loop); masm.testl(result, result); masm.jcc(ConditionFlag.Zero, trueLabel); + if (requiresNaNCheck) { + Label unalignedCheck = new Label(); + masm.jmpb(unalignedCheck); + masm.bind(nanCheck); + emitFloatCompareWithinRange(crb, masm, array1, array2, length, 0, falseLabel, AVX_VECTOR_SIZE); + masm.jmpb(loopCheck); + masm.bind(unalignedCheck); + } + /* * Compare the remaining bytes with an unaligned memory load aligned to the end of the * array. @@ -264,7 +314,12 @@ masm.vmovdqu(vector2, new AMD64Address(array2, result, Scale.Times1, -AVX_VECTOR_SIZE)); masm.vpxor(vector1, vector1, vector2); masm.vptest(vector1, vector1); - masm.jcc(ConditionFlag.NotZero, falseLabel); + if (requiresNaNCheck) { + masm.jcc(ConditionFlag.Zero, trueLabel); + emitFloatCompareWithinRange(crb, masm, array1, array2, result, -AVX_VECTOR_SIZE, falseLabel, AVX_VECTOR_SIZE); + } else { + masm.jcc(ConditionFlag.NotZero, falseLabel); + } masm.jmp(trueLabel); masm.bind(compareTail); @@ -283,11 +338,15 @@ Label loop = new Label(); Label compareTail = new Label(); + boolean requiresNaNCheck = kind.isNumericFloat(); + Label loopCheck = new Label(); + Label nanCheck = new Label(); + Register temp = asRegister(temp4); masm.andl(result, VECTOR_SIZE - 1); // tail count (in bytes) masm.andl(length, ~(VECTOR_SIZE - 1)); // vector count (in bytes) - masm.jccb(ConditionFlag.Zero, compareTail); + masm.jcc(ConditionFlag.Zero, compareTail); masm.leaq(array1, new AMD64Address(array1, length, Scale.Times1, 0)); masm.leaq(array2, new AMD64Address(array2, length, Scale.Times1, 0)); @@ -298,12 +357,27 @@ masm.bind(loop); masm.movq(temp, new AMD64Address(array1, length, Scale.Times1, 0)); masm.cmpq(temp, new AMD64Address(array2, length, Scale.Times1, 0)); - masm.jccb(ConditionFlag.NotEqual, falseLabel); + masm.jcc(ConditionFlag.NotEqual, requiresNaNCheck ? nanCheck : falseLabel); + + masm.bind(loopCheck); masm.addq(length, VECTOR_SIZE); masm.jccb(ConditionFlag.NotZero, loop); masm.testl(result, result); - masm.jccb(ConditionFlag.Zero, trueLabel); + masm.jcc(ConditionFlag.Zero, trueLabel); + + if (requiresNaNCheck) { + // NaN check is slow path and hence placed outside of the main loop. + Label unalignedCheck = new Label(); + masm.jmpb(unalignedCheck); + masm.bind(nanCheck); + // At most two iterations, unroll in the emitted code. + for (int offset = 0; offset < VECTOR_SIZE; offset += kind.getByteCount()) { + emitFloatCompare(masm, array1, array2, length, offset, falseLabel, kind.getByteCount() == VECTOR_SIZE); + } + masm.jmpb(loopCheck); + masm.bind(unalignedCheck); + } /* * Compare the remaining bytes with an unaligned memory load aligned to the end of the @@ -311,7 +385,15 @@ */ masm.movq(temp, new AMD64Address(array1, result, Scale.Times1, -VECTOR_SIZE)); masm.cmpq(temp, new AMD64Address(array2, result, Scale.Times1, -VECTOR_SIZE)); - masm.jccb(ConditionFlag.NotEqual, falseLabel); + if (requiresNaNCheck) { + masm.jcc(ConditionFlag.Equal, trueLabel); + // At most two iterations, unroll in the emitted code. + for (int offset = 0; offset < VECTOR_SIZE; offset += kind.getByteCount()) { + emitFloatCompare(masm, array1, array2, result, -VECTOR_SIZE + offset, falseLabel, kind.getByteCount() == VECTOR_SIZE); + } + } else { + masm.jccb(ConditionFlag.NotEqual, falseLabel); + } masm.jmpb(trueLabel); masm.bind(compareTail); @@ -333,8 +415,13 @@ masm.jccb(ConditionFlag.Zero, compare2Bytes); masm.movl(temp, new AMD64Address(array1, 0)); masm.cmpl(temp, new AMD64Address(array2, 0)); - masm.jccb(ConditionFlag.NotEqual, falseLabel); - + if (kind == JavaKind.Float) { + masm.jccb(ConditionFlag.Equal, trueLabel); + emitFloatCompare(masm, array1, array2, Register.None, 0, falseLabel, true); + masm.jmpb(trueLabel); + } else { + masm.jccb(ConditionFlag.NotEqual, falseLabel); + } if (kind.getByteCount() <= 2) { // Move array pointers forward. masm.leaq(array1, new AMD64Address(array1, 4)); @@ -372,6 +459,71 @@ } } + /** + * Emits code to fall through if {@code src} is NaN, otherwise jump to {@code branchOrdered}. + */ + private void emitNaNCheck(AMD64MacroAssembler masm, AMD64Address src, Label branchIfNonNaN) { + assert kind.isNumericFloat(); + Register tempXMMReg = asRegister(tempXMM); + if (kind == JavaKind.Float) { + masm.movflt(tempXMMReg, src); + } else { + masm.movdbl(tempXMMReg, src); + } + SSEOp.UCOMIS.emit(masm, kind == JavaKind.Float ? OperandSize.PS : OperandSize.PD, tempXMMReg, tempXMMReg); + masm.jcc(ConditionFlag.NoParity, branchIfNonNaN); + } + + /** + * Emits code to compare if two floats are bitwise equal or both NaN. + */ + private void emitFloatCompare(AMD64MacroAssembler masm, Register base1, Register base2, Register index, int offset, Label falseLabel, boolean skipBitwiseCompare) { + AMD64Address address1 = new AMD64Address(base1, index, Scale.Times1, offset); + AMD64Address address2 = new AMD64Address(base2, index, Scale.Times1, offset); + + Label bitwiseEqual = new Label(); + + if (!skipBitwiseCompare) { + // Bitwise compare + Register temp = asRegister(temp4); + + if (kind == JavaKind.Float) { + masm.movl(temp, address1); + masm.cmpl(temp, address2); + } else { + masm.movq(temp, address1); + masm.cmpq(temp, address2); + } + masm.jccb(ConditionFlag.Equal, bitwiseEqual); + } + + emitNaNCheck(masm, address1, falseLabel); + emitNaNCheck(masm, address2, falseLabel); + + masm.bind(bitwiseEqual); + } + + /** + * Emits code to compare float equality within a range. + */ + private void emitFloatCompareWithinRange(CompilationResultBuilder crb, AMD64MacroAssembler masm, Register base1, Register base2, Register index, int offset, Label falseLabel, int range) { + assert kind.isNumericFloat(); + Label loop = new Label(); + Register i = asRegister(temp5); + + masm.movq(i, range); + masm.negq(i); + // Align the main loop + masm.align(crb.target.wordSize * 2); + masm.bind(loop); + emitFloatCompare(masm, base1, base2, index, offset, falseLabel, kind.getByteCount() == range); + masm.addq(index, kind.getByteCount()); + masm.addq(i, kind.getByteCount()); + masm.jccb(ConditionFlag.NotZero, loop); + // Floats within the range are equal, revert change to the register index + masm.subq(index, range); + } + private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArrayEqualsOp.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArrayEqualsOp.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArrayEqualsOp.java Wed Aug 23 20:20:59 2017 +0000 @@ -78,6 +78,8 @@ public SPARCArrayEqualsOp(LIRGeneratorTool tool, JavaKind kind, AllocatableValue result, AllocatableValue array1, AllocatableValue array2, AllocatableValue length) { super(TYPE, SIZE); + + assert !kind.isNumericFloat() : "Float arrays comparison (bitwise_equal || both_NaN) isn't supported"; this.kind = kind; Class arrayClass = Array.newInstance(kind.toJavaClass(), 0).getClass(); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/gen/ArithmeticLIRGenerator.java Wed Aug 23 20:20:59 2017 +0000 @@ -59,7 +59,7 @@ if (isNumericInteger(a.getPlatformKind())) { LIRKind aKind = a.getValueKind(LIRKind.class); LIRKind bKind = b.getValueKind(LIRKind.class); - assert a.getPlatformKind() == b.getPlatformKind(); + assert a.getPlatformKind() == b.getPlatformKind() : a.getPlatformKind() + " vs. " + b.getPlatformKind(); if (aKind.isUnknownReference()) { resultKind = aKind; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPartialUnrollPhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPartialUnrollPhase.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPartialUnrollPhase.java Wed Aug 23 20:20:59 2017 +0000 @@ -51,6 +51,8 @@ try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) { LoopsData dataCounted = new LoopsData(graph); dataCounted.detectedCountedLoops(); + Graph.Mark mark = graph.getMark(); + boolean prePostInserted = false; for (LoopEx loop : dataCounted.countedLoops()) { if (!LoopTransformations.isUnrollableLoop(loop)) { continue; @@ -59,9 +61,10 @@ if (loop.loopBegin().isSimpleLoop()) { // First perform the pre/post transformation and do the partial // unroll when we come around again. - LoopTransformations.insertPrePostLoops(loop, graph); + LoopTransformations.insertPrePostLoops(loop); + prePostInserted = true; } else { - LoopTransformations.partialUnroll(loop, graph); + LoopTransformations.partialUnroll(loop); } changed = true; } @@ -72,11 +75,25 @@ canonicalizer.applyIncremental(graph, context, listener.getNodes()); listener.getNodes().clear(); } + + assert !prePostInserted || checkCounted(graph, mark); } } } } + private static boolean checkCounted(StructuredGraph graph, Graph.Mark mark) { + LoopsData dataCounted; + dataCounted = new LoopsData(graph); + dataCounted.detectedCountedLoops(); + for (LoopEx anyLoop : dataCounted.loops()) { + if (graph.isNew(mark, anyLoop.loopBegin())) { + assert anyLoop.isCounted() : "pre/post transformation loses counted loop " + anyLoop.loopBegin(); + } + } + return true; + } + @Override public boolean checkContract() { return false; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java Wed Aug 23 20:20:59 2017 +0000 @@ -31,6 +31,7 @@ import java.util.List; import org.graalvm.compiler.core.common.RetryableBailoutException; +import org.graalvm.compiler.core.common.calc.Condition; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Graph.Mark; @@ -145,9 +146,9 @@ // TODO (gd) probabilities need some amount of fixup.. (probably also in other transforms) } - public static void partialUnroll(LoopEx loop, StructuredGraph graph) { + public static void partialUnroll(LoopEx loop) { assert loop.loopBegin().isMainLoop(); - graph.getDebug().log("LoopPartialUnroll %s", loop); + loop.loopBegin().graph().getDebug().log("LoopPartialUnroll %s", loop); LoopFragmentInside newSegment = loop.inside().duplicate(); newSegment.insertWithinAfter(loop); @@ -222,72 +223,73 @@ // The pre loop is constrained to one iteration for now and will likely // be updated to produce vector alignment if applicable. - public static void insertPrePostLoops(LoopEx loop, StructuredGraph graph) { + public static LoopBeginNode insertPrePostLoops(LoopEx loop) { + StructuredGraph graph = loop.loopBegin().graph(); graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop); LoopFragmentWhole preLoop = loop.whole(); CountedLoopInfo preCounted = loop.counted(); IfNode preLimit = preCounted.getLimitTest(); - if (preLimit != null) { - LoopBeginNode preLoopBegin = loop.loopBegin(); - InductionVariable preIv = preCounted.getCounter(); - LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit(); - FixedNode continuationNode = preLoopExitNode.next(); + assert preLimit != null; + LoopBeginNode preLoopBegin = loop.loopBegin(); + InductionVariable preIv = preCounted.getCounter(); + LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit(); + FixedNode continuationNode = preLoopExitNode.next(); - // Each duplication is inserted after the original, ergo create the post loop first - LoopFragmentWhole mainLoop = preLoop.duplicate(); - LoopFragmentWhole postLoop = preLoop.duplicate(); - preLoopBegin.incrementSplits(); - preLoopBegin.incrementSplits(); - preLoopBegin.setPreLoop(); - graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication"); - LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin); - mainLoopBegin.setMainLoop(); - LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin); - postLoopBegin.setPostLoop(); + // Each duplication is inserted after the original, ergo create the post loop first + LoopFragmentWhole mainLoop = preLoop.duplicate(); + LoopFragmentWhole postLoop = preLoop.duplicate(); + preLoopBegin.incrementSplits(); + preLoopBegin.incrementSplits(); + preLoopBegin.setPreLoop(); + graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication"); + LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin); + mainLoopBegin.setMainLoop(); + LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin); + postLoopBegin.setPostLoop(); - EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin); - AbstractMergeNode postMergeNode = postEndNode.merge(); - LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit(); + EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin); + AbstractMergeNode postMergeNode = postEndNode.merge(); + LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit(); - // Update the main loop phi initialization to carry from the pre loop - for (PhiNode prePhiNode : preLoopBegin.phis()) { - PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode); - mainPhiNode.setValueAt(0, prePhiNode); - } + // Update the main loop phi initialization to carry from the pre loop + for (PhiNode prePhiNode : preLoopBegin.phis()) { + PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode); + mainPhiNode.setValueAt(0, prePhiNode); + } - EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin); - AbstractMergeNode mainMergeNode = mainEndNode.merge(); - AbstractEndNode postEntryNode = postLoopBegin.forwardEnd(); + EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin); + AbstractMergeNode mainMergeNode = mainEndNode.merge(); + AbstractEndNode postEntryNode = postLoopBegin.forwardEnd(); - // In the case of no Bounds tests, we just flow right into the main loop - AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode); - LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit(); - mainLoopExitNode.setNext(mainLandingNode); - preLoopExitNode.setNext(mainLoopBegin.forwardEnd()); + // In the case of no Bounds tests, we just flow right into the main loop + AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode); + LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit(); + mainLoopExitNode.setNext(mainLandingNode); + preLoopExitNode.setNext(mainLoopBegin.forwardEnd()); - // Add and update any phi edges as per merge usage as needed and update usages - processPreLoopPhis(loop, mainLoop, postLoop); - continuationNode.predecessor().clearSuccessors(); - postLoopExitNode.setNext(continuationNode); - cleanupMerge(postMergeNode, postLoopExitNode); - cleanupMerge(mainMergeNode, mainLandingNode); + // Add and update any phi edges as per merge usage as needed and update usages + processPreLoopPhis(loop, mainLoop, postLoop); + continuationNode.predecessor().clearSuccessors(); + postLoopExitNode.setNext(continuationNode); + cleanupMerge(postMergeNode, postLoopExitNode); + cleanupMerge(mainMergeNode, mainLandingNode); - // Change the preLoop to execute one iteration for now - updateMainLoopLimit(preLimit, preIv, mainLoop); - updatePreLoopLimit(preLimit, preIv, preCounted); - preLoopBegin.setLoopFrequency(1); - mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2)); - postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1)); + // Change the preLoop to execute one iteration for now + updateMainLoopLimit(preLimit, preIv, mainLoop); + updatePreLoopLimit(preLimit, preIv, preCounted); + preLoopBegin.setLoopFrequency(1); + mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2)); + postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1)); - // The pre and post loops don't require safepoints at all - for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) { - graph.removeFixed(safepoint); - } - for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) { - graph.removeFixed(safepoint); - } + // The pre and post loops don't require safepoints at all + for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) { + graph.removeFixed(safepoint); + } + for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) { + graph.removeFixed(safepoint); } graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop); + return mainLoopBegin; } /** @@ -373,7 +375,7 @@ throw GraalError.shouldNotReachHere(); } - // Preloop always performs at least once iteration, so remove that from the main loop. + // Preloop always performs at least one iteration, so remove that from the main loop. ValueNode newLimit = sub(graph, ub, mainStride); // Re-wire the condition with the new limit @@ -445,6 +447,14 @@ return false; } LoopBeginNode loopBegin = loop.loopBegin(); + LogicNode condition = loop.counted().getLimitTest().condition(); + if (!(condition instanceof CompareNode)) { + return false; + } + if (((CompareNode) condition).condition() == Condition.EQ || ((CompareNode) condition).condition() == Condition.NE) { + condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s condition unsupported %s ", loopBegin, ((CompareNode) condition).condition()); + return false; + } if (loopBegin.isMainLoop() || loopBegin.isSimpleLoop()) { // Flow-less loops to partial unroll for now. 3 blocks corresponds to an if that either // exits or continues the loop. There might be fixed and floating work within the loop @@ -452,6 +462,7 @@ if (loop.loop().getBlocks().size() < 3) { return true; } + condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s too large to unroll %s ", loopBegin, loop.loop().getBlocks().size()); } return false; } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -22,12 +22,41 @@ */ package org.graalvm.compiler.loop.test; +import java.util.ListIterator; + +import org.graalvm.compiler.core.common.CompilationIdentifier; import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.java.ComputeLoopFrequenciesClosure; +import org.graalvm.compiler.loop.DefaultLoopPolicies; +import org.graalvm.compiler.loop.LoopEx; +import org.graalvm.compiler.loop.LoopFragmentInside; +import org.graalvm.compiler.loop.LoopsData; +import org.graalvm.compiler.loop.phases.LoopPartialUnrollPhase; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.phases.BasePhase; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.PhaseSuite; +import org.graalvm.compiler.phases.common.CanonicalizerPhase; +import org.graalvm.compiler.phases.common.ConditionalEliminationPhase; +import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; +import org.graalvm.compiler.phases.common.DeoptimizationGroupingPhase; +import org.graalvm.compiler.phases.common.FloatingReadPhase; +import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase; +import org.graalvm.compiler.phases.common.GuardLoweringPhase; +import org.graalvm.compiler.phases.common.LoweringPhase; +import org.graalvm.compiler.phases.common.RemoveValueProxyPhase; +import org.graalvm.compiler.phases.tiers.MidTierContext; +import org.graalvm.compiler.phases.tiers.Suites; +import org.junit.Ignore; import org.junit.Test; +import jdk.vm.ci.meta.ResolvedJavaMethod; + public class LoopPartialUnrollTest extends GraalCompilerTest { @Override @@ -41,100 +70,72 @@ return false; } - public static long testMultiplySnippet(int arg) { - long r = 1; - for (int i = 0; branchProbability(0.99, i < arg); i++) { - r += r * i; + public static long sumWithEqualityLimit(int[] text) { + long sum = 0; + for (int i = 0; branchProbability(0.99, i != text.length); ++i) { + sum += volatileInt; } - return r; - } - - @Test - public void testMultiply() { - test("testMultiplySnippet", 9); - } - - public static int testNestedSumSnippet(int d) { - int c = 0; - for (int i = 0; i < d; i++) { - for (int j = 0; branchProbability(0.99, j < i); j++) { - c += c + j & 0x3; - } - } - return c; - } - - @Test - public void testNestedSumBy2() { - for (int i = 0; i < 1000; i++) { - test("testNestedSumBy2Snippet", i); - } + return sum; } - public static int testNestedSumBy2Snippet(int d) { - int c = 0; - for (int i = 0; i < d; i++) { - for (int j = 0; branchProbability(0.99, j < i); j += 2) { - c += c + j & 0x3; - } - } - return c; - } - + @Ignore("equality limits aren't working properly") @Test - public void testNestedSum() { - for (int i = 0; i < 1000; i++) { - test("testNestedSumSnippet", i); - } - } - - public static int testSumDownSnippet(int d) { - int c = 0; - for (int j = d; branchProbability(0.99, j > -4); j--) { - c += c + j & 0x3; - } - return c; - } - - @Test - public void testSumDown() { - test("testSumDownSnippet", 1); - for (int i = 0; i < 160; i++) { - test("testSumDownSnippet", i); - } - } - - public static int testSumDownBy2Snippet(int d) { - int c = 0; - for (int j = d; branchProbability(0.99, j > -4); j -= 2) { - c += c + j & 0x3; - } - return c; - } - - @Test - public void testSumDownBy2() { - test("testSumDownBy2Snippet", 1); - for (int i = 0; i < 160; i++) { - test("testSumDownBy2Snippet", i); + public void testSumWithEqualityLimit() { + for (int i = 0; i < 128; i++) { + int[] data = new int[i]; + test("sumWithEqualityLimit", data); } } @Test public void testLoopCarried() { - test("testLoopCarriedSnippet", 1, 2); - test("testLoopCarriedSnippet", 0, 4); - test("testLoopCarriedSnippet", 4, 0); + for (int i = 0; i < 64; i++) { + test("testLoopCarriedSnippet", i); + } + } + + @Test + public void testLoopCarriedDuplication() { + testDuplicateBody("testLoopCarriedReference", "testLoopCarriedSnippet"); } - public static int testLoopCarriedSnippet(int a, int b) { - int c = a; - int d = b; - for (int j = 0; branchProbability(0.99, j < a); j++) { - d = c; - c += 1; + static volatile int volatileInt = 3; + + public int testLoopCarriedSnippet(int iterations) { + int a = 0; + int b = 0; + int c = 0; + + for (int i = 0; branchProbability(0.99, i < iterations); i++) { + int t1 = volatileInt; + int t2 = a + b; + c = b; + b = a; + a = t1 + t2; } - return c + d; + + return c; + } + + public int testLoopCarriedReference(int iterations) { + int a = 0; + int b = 0; + int c = 0; + + for (int i = 0; branchProbability(0.99, i < iterations); i += 2) { + int t1 = volatileInt; + int t2 = a + b; + c = b; + b = a; + a = t1 + t2; + t1 = volatileInt; + t2 = a + b; + c = b; + b = a; + a = t1 + t2; + } + + return c; } public static long init = Runtime.getRuntime().totalMemory(); @@ -181,4 +182,82 @@ public void testSignExtension() { test("testSignExtensionSnippet", 9L); } + + @Override + protected Suites createSuites(OptionValues opts) { + Suites suites = super.createSuites(opts).copy(); + PhaseSuite mid = suites.getMidTier(); + ListIterator> iter = mid.findPhase(LoopPartialUnrollPhase.class); + BasePhase partialUnoll = iter.previous(); + if (iter.previous().getClass() != FrameStateAssignmentPhase.class) { + // Ensure LoopPartialUnrollPhase runs immediately after FrameStateAssignment, so it gets + // priority over other optimizations in these tests. + mid.findPhase(LoopPartialUnrollPhase.class).remove(); + ListIterator> fsa = mid.findPhase(FrameStateAssignmentPhase.class); + fsa.add(partialUnoll); + } + return suites; + } + + public void testGraph(String reference, String test) { + StructuredGraph referenceGraph = buildGraph(reference, false); + StructuredGraph testGraph = buildGraph(test, true); + assertEquals(referenceGraph, testGraph, false, false); + } + + @SuppressWarnings("try") + public StructuredGraph buildGraph(String name, boolean partialUnroll) { + CompilationIdentifier id = new CompilationIdentifier() { + @Override + public String toString(Verbosity verbosity) { + return name; + } + }; + ResolvedJavaMethod method = getResolvedJavaMethod(name); + OptionValues options = new OptionValues(getInitialOptions(), DefaultLoopPolicies.UnrollMaxIterations, 2); + StructuredGraph graph = parse(builder(method, StructuredGraph.AllowAssumptions.YES, id, options), getEagerGraphBuilderSuite()); + try (DebugContext.Scope buildScope = graph.getDebug().scope(name, method, graph)) { + MidTierContext context = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null); + + CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); + canonicalizer.apply(graph, context); + new RemoveValueProxyPhase().apply(graph); + new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context); + new FloatingReadPhase().apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new ConditionalEliminationPhase(true).apply(graph, context); + ComputeLoopFrequenciesClosure.compute(graph); + new GuardLoweringPhase().apply(graph, context); + new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context); + new FrameStateAssignmentPhase().apply(graph); + new DeoptimizationGroupingPhase().apply(graph, context); + canonicalizer.apply(graph, context); + new ConditionalEliminationPhase(true).apply(graph, context); + if (partialUnroll) { + LoopsData dataCounted = new LoopsData(graph); + dataCounted.detectedCountedLoops(); + for (LoopEx loop : dataCounted.countedLoops()) { + LoopFragmentInside newSegment = loop.inside().duplicate(); + newSegment.insertWithinAfter(loop, false); + } + canonicalizer.apply(graph, getDefaultMidTierContext()); + } + new DeadCodeEliminationPhase().apply(graph); + canonicalizer.apply(graph, context); + graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "before compare"); + return graph; + } catch (Throwable e) { + throw getDebugContext().handle(e); + } + } + + public void testDuplicateBody(String reference, String test) { + + StructuredGraph referenceGraph = buildGraph(reference, false); + StructuredGraph testGraph = buildGraph(test, true); + CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); + canonicalizer.apply(testGraph, getDefaultMidTierContext()); + canonicalizer.apply(referenceGraph, getDefaultMidTierContext()); + assertEquals(referenceGraph, testGraph); + } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java Wed Aug 23 20:20:59 2017 +0000 @@ -100,11 +100,12 @@ @Override public boolean shouldPartiallyUnroll(LoopEx loop) { + LoopBeginNode loopBegin = loop.loopBegin(); if (!loop.isCounted()) { + loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s isn't counted", loopBegin); return false; } OptionValues options = loop.entryPoint().getOptions(); - LoopBeginNode loopBegin = loop.loopBegin(); int maxNodes = ExactPartialUnrollMaxNodes.getValue(options); maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount())); int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count()); @@ -112,6 +113,7 @@ if (unrollFactor == 1) { double loopFrequency = loopBegin.loopFrequency(); if (loopBegin.isSimpleLoop() && loopFrequency < 5.0) { + loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s frequency too low %s ", loopBegin, loopFrequency); return false; } loopBegin.setLoopOrigFrequency(loopFrequency); @@ -136,6 +138,7 @@ } return true; } else { + loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s unrolled loop is too large %s ", loopBegin, size); return false; } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java Wed Aug 23 20:20:59 2017 +0000 @@ -22,6 +22,7 @@ */ package org.graalvm.compiler.loop; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -142,18 +143,49 @@ end.setNext(loop.entryPoint()); } + /** + * Duplicate the body within the loop after the current copy copy of the body, updating the + * iteration limit to account for the duplication. + * + * @param loop + */ public void insertWithinAfter(LoopEx loop) { + insertWithinAfter(loop, true); + } + + /** + * Duplicate the body within the loop after the current copy copy of the body. + * + * @param loop + * @param updateLimit true if the iteration limit should be adjusted. + */ + public void insertWithinAfter(LoopEx loop, boolean updateLimit) { assert isDuplicate() && original().loop() == loop; patchNodes(dataFixWithinAfter); + /* + * Collect any new back edges values before updating them since they might reference each + * other. + */ LoopBeginNode mainLoopBegin = loop.loopBegin(); + ArrayList backedgeValues = new ArrayList<>(); for (PhiNode mainPhiNode : mainLoopBegin.phis()) { ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1)); + if (duplicatedNode == null) { + if (mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1))) { + duplicatedNode = ((PhiNode) (mainPhiNode.valueAt(1))).valueAt(1); + } else { + assert mainPhiNode.valueAt(1).isConstant() : mainPhiNode.valueAt(1); + } + } + backedgeValues.add(duplicatedNode); + } + int index = 0; + for (PhiNode mainPhiNode : mainLoopBegin.phis()) { + ValueNode duplicatedNode = backedgeValues.get(index++); if (duplicatedNode != null) { mainPhiNode.setValueAt(1, duplicatedNode); - } else { - assert mainPhiNode.valueAt(1).isConstant() || mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1)) : mainPhiNode.valueAt(1); } } @@ -166,27 +198,29 @@ } int unrollFactor = mainLoopBegin.getUnrollFactor(); - - // Now use the previous unrollFactor to update the exit condition to power of two StructuredGraph graph = mainLoopBegin.graph(); - InductionVariable iv = loop.counted().getCounter(); - CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition(); - ValueNode compareBound; - if (compareNode.getX() == iv.valueNode()) { - compareBound = compareNode.getY(); - } else if (compareNode.getY() == iv.valueNode()) { - compareBound = compareNode.getX(); - } else { - throw GraalError.shouldNotReachHere(); - } - if (iv.direction() == InductionVariable.Direction.Up) { - ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * iv.constantStride())); - ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal)); - compareNode.replaceFirstInput(compareBound, newLimit); - } else if (iv.direction() == InductionVariable.Direction.Down) { - ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -iv.constantStride())); - ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal)); - compareNode.replaceFirstInput(compareBound, newLimit); + if (updateLimit) { + // Now use the previous unrollFactor to update the exit condition to power of two + InductionVariable iv = loop.counted().getCounter(); + CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition(); + ValueNode compareBound; + if (compareNode.getX() == iv.valueNode()) { + compareBound = compareNode.getY(); + } else if (compareNode.getY() == iv.valueNode()) { + compareBound = compareNode.getX(); + } else { + throw GraalError.shouldNotReachHere(); + } + long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor; + if (iv.direction() == InductionVariable.Direction.Up) { + ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * originalStride)); + ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal)); + compareNode.replaceFirstInput(compareBound, newLimit); + } else if (iv.direction() == InductionVariable.Direction.Down) { + ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -originalStride)); + ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal)); + compareNode.replaceFirstInput(compareBound, newLimit); + } } mainLoopBegin.setUnrollFactor(unrollFactor * 2); mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/TestJMH.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/TestJMH.java Wed Aug 23 13:14:20 2017 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2015, 2015, 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 org.graalvm.compiler.microbenchmarks.graal; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Warmup; - -@Warmup(iterations = 1) -@Measurement(iterations = 1) -@Fork(1) -/** - * This dummy class is used to verify that the JMH microbenchmarking environment is set up properly. - */ -public class TestJMH { - - @Benchmark - public void testJMH() { - // This method was intentionally left blank. - } - -} diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/TestJMHWhitebox.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/TestJMHWhitebox.java Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 2017, 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 org.graalvm.compiler.microbenchmarks.graal; + +import org.graalvm.compiler.microbenchmarks.graal.util.GraalState; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Warmup; + +@Warmup(iterations = 1) +@Measurement(iterations = 1) +@Fork(1) +/** + * This dummy class is used to verify that the JMH microbenchmarking environment is set up properly. + */ +public class TestJMHWhitebox { + + @Benchmark + public void testJMH(@SuppressWarnings("unused") GraalState s) { + // This method was intentionally left blank. + } + +} diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,12 +25,10 @@ import static org.graalvm.compiler.core.test.GraalCompilerTest.getInitialOptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import org.junit.Before; -import org.junit.Test; +import java.math.BigInteger; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.IntegerConvertOp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp; import org.graalvm.compiler.core.common.type.IntegerStamp; @@ -42,6 +40,12 @@ import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.options.OptionValues; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; /** * This class tests that integer stamps are created correctly for constants. @@ -365,10 +369,187 @@ assertEquals(IntegerStamp.create(32, 0, 0x1ff, 0, 0x1ff), shl.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1))); assertEquals(IntegerStamp.create(32, 0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); assertEquals(IntegerStamp.create(32, 0x1e0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(32, -4096, -4096, -4096, -4096), shl.foldStamp(IntegerStamp.create(32, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8))); + assertEquals(StampFactory.empty(JavaKind.Int), shl.foldStamp(StampFactory.empty(JavaKind.Int), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(StampFactory.empty(JavaKind.Int), shl.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int))); assertEquals(IntegerStamp.create(64, 0, 0x1ff, 0, 0x1ff), shl.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1))); assertEquals(IntegerStamp.create(64, 0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); assertEquals(IntegerStamp.create(64, 0x1e0, 0x1fe0, 0, 0x1fe0), shl.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(64, -4096, -4096, -4096, -4096), shl.foldStamp(IntegerStamp.create(64, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8))); + assertEquals(StampFactory.empty(JavaKind.Long), shl.foldStamp(StampFactory.empty(JavaKind.Long), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(StampFactory.empty(JavaKind.Long), shl.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int))); + } + @Test + public void testUnsignedShiftRight() { + ShiftOp ushr = IntegerStamp.OPS.getUShr(); + assertEquals(IntegerStamp.create(32, 0, 0xff, 0, 0xff), ushr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1))); + assertEquals(IntegerStamp.create(32, 0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(32, 0x0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(32, 0xffffff, 0xffffff, 0xffffff, 0xffffff), ushr.foldStamp(IntegerStamp.create(32, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8))); + assertEquals(StampFactory.empty(JavaKind.Int), ushr.foldStamp(StampFactory.empty(JavaKind.Int), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(StampFactory.empty(JavaKind.Int), ushr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int))); + + assertEquals(IntegerStamp.create(64, 0, 0xff, 0, 0xff), ushr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1))); + assertEquals(IntegerStamp.create(64, 0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(64, 0x0, 0x07, 0, 0x07), ushr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(64, 0xffffffffffffffL, 0xffffffffffffffL, 0xffffffffffffffL, 0xffffffffffffffL), + ushr.foldStamp(IntegerStamp.create(64, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8))); + assertEquals(StampFactory.empty(JavaKind.Long), ushr.foldStamp(StampFactory.empty(JavaKind.Long), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(StampFactory.empty(JavaKind.Long), ushr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int))); + } + + @Test + public void testShiftRight() { + ShiftOp shr = IntegerStamp.OPS.getShr(); + assertEquals(IntegerStamp.create(32, 0, 0xff, 0, 0xff), shr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1))); + assertEquals(IntegerStamp.create(32, 0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(32, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(32, 0x0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(32, -1, -1, -1, -1), shr.foldStamp(IntegerStamp.create(32, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8))); + assertEquals(StampFactory.empty(JavaKind.Int), shr.foldStamp(StampFactory.empty(JavaKind.Int), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(StampFactory.empty(JavaKind.Int), shr.foldStamp(IntegerStamp.create(32, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int))); + + assertEquals(IntegerStamp.create(64, 0, 0xff, 0, 0xff), shr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 0, 1, 0, 1))); + assertEquals(IntegerStamp.create(64, 0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(64, 0, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(64, 0x0, 0x07, 0, 0x07), shr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(IntegerStamp.create(64, -1, -1, -1, -1), shr.foldStamp(IntegerStamp.create(64, -16, -16, -16, -16), IntegerStamp.create(32, 8, 8, 8, 8))); + assertEquals(StampFactory.empty(JavaKind.Long), shr.foldStamp(StampFactory.empty(JavaKind.Long), IntegerStamp.create(32, 5, 5, 5, 5))); + assertEquals(StampFactory.empty(JavaKind.Long), shr.foldStamp(IntegerStamp.create(64, 0xf, 0xff, 0, 0xff), (IntegerStamp) StampFactory.empty(JavaKind.Int))); + } + + @Test + public void testMulHigh() { + testSomeMulHigh(IntegerStamp.OPS.getMulHigh()); + } + + @Test + public void testUMulHigh() { + testSomeMulHigh(IntegerStamp.OPS.getUMulHigh()); + } + + private static void testSomeMulHigh(BinaryOp someMulHigh) { + // 32 bits + testMulHigh(someMulHigh, 0, 0, 32); + + testMulHigh(someMulHigh, 1, 1, 32); + testMulHigh(someMulHigh, 1, 5, 32); + testMulHigh(someMulHigh, 256, 256, 32); + testMulHigh(someMulHigh, 0xFFFFFFF, 0xFFFFFFA, 32); + testMulHigh(someMulHigh, Integer.MAX_VALUE, 2, 32); + + testMulHigh(someMulHigh, -1, -1, 32); + testMulHigh(someMulHigh, -1, -5, 32); + testMulHigh(someMulHigh, -256, -256, 32); + testMulHigh(someMulHigh, -0xFFFFFFF, -0xFFFFFFA, 32); + testMulHigh(someMulHigh, Integer.MIN_VALUE, -2, 32); + + testMulHigh(someMulHigh, -1, 1, 32); + testMulHigh(someMulHigh, -1, 5, 32); + testMulHigh(someMulHigh, -256, 256, 32); + testMulHigh(someMulHigh, -0xFFFFFFF, 0xFFFFFFA, 32); + testMulHigh(someMulHigh, Integer.MIN_VALUE, 2, 32); + + testMulHigh(someMulHigh, Integer.MIN_VALUE, Integer.MIN_VALUE, 32); + testMulHigh(someMulHigh, Integer.MAX_VALUE, Integer.MAX_VALUE, 32); + + assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), StampFactory.forKind(JavaKind.Int).empty())); + assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), StampFactory.forKind(JavaKind.Int).unrestricted())); + assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), IntegerStamp.create(32, 0, 0))); + assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), IntegerStamp.create(32, 1, 1))); + assertEquals(StampFactory.forKind(JavaKind.Int).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).empty(), IntegerStamp.create(32, -1, -1))); + + assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), StampFactory.forKind(JavaKind.Int).unrestricted())); + assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), IntegerStamp.create(32, 0, 0))); + assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), IntegerStamp.create(32, 1, 1))); + assertEquals(StampFactory.forKind(JavaKind.Int).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Int).unrestricted(), IntegerStamp.create(32, -1, -1))); + + // 64 bits + testMulHigh(someMulHigh, 0, 0, 64); + + testMulHigh(someMulHigh, 1, 1, 64); + testMulHigh(someMulHigh, 1, 5, 64); + testMulHigh(someMulHigh, 256, 256, 64); + testMulHigh(someMulHigh, 0xFFFFFFF, 0xFFFFFFA, 64); + testMulHigh(someMulHigh, 0xFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFAL, 64); + testMulHigh(someMulHigh, Integer.MAX_VALUE, 2, 64); + testMulHigh(someMulHigh, Long.MAX_VALUE, 2, 64); + + testMulHigh(someMulHigh, -1, -1, 64); + testMulHigh(someMulHigh, -1, -5, 64); + testMulHigh(someMulHigh, -256, -256, 64); + testMulHigh(someMulHigh, -0xFFFFFFF, -0xFFFFFFA, 64); + testMulHigh(someMulHigh, -0xFFFFFFFFFFFFFFL, -0xFFFFFFFFFFFFFAL, 64); + testMulHigh(someMulHigh, Integer.MIN_VALUE, -2, 64); + testMulHigh(someMulHigh, Long.MIN_VALUE, -2, 64); + + testMulHigh(someMulHigh, -1, 1, 64); + testMulHigh(someMulHigh, -1, 5, 64); + testMulHigh(someMulHigh, -256, 256, 64); + testMulHigh(someMulHigh, -0xFFFFFFF, 0xFFFFFFA, 64); + testMulHigh(someMulHigh, -0xFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFAL, 64); + testMulHigh(someMulHigh, Integer.MIN_VALUE, 2, 64); + testMulHigh(someMulHigh, Long.MIN_VALUE, 2, 64); + + testMulHigh(someMulHigh, Integer.MIN_VALUE, Integer.MIN_VALUE, 64); + testMulHigh(someMulHigh, Long.MIN_VALUE, Long.MIN_VALUE, 64); + testMulHigh(someMulHigh, Integer.MAX_VALUE, Integer.MAX_VALUE, 64); + testMulHigh(someMulHigh, Long.MAX_VALUE, Long.MAX_VALUE, 64); + + assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), StampFactory.forKind(JavaKind.Long).empty())); + assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), StampFactory.forKind(JavaKind.Long).unrestricted())); + assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), IntegerStamp.create(64, 0, 0))); + assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), IntegerStamp.create(64, 1, 1))); + assertEquals(StampFactory.forKind(JavaKind.Long).empty(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).empty(), IntegerStamp.create(64, -1, -1))); + + assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), StampFactory.forKind(JavaKind.Long).unrestricted())); + assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), IntegerStamp.create(64, 0, 0))); + assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), IntegerStamp.create(64, 1, 1))); + assertEquals(StampFactory.forKind(JavaKind.Long).unrestricted(), someMulHigh.foldStamp(StampFactory.forKind(JavaKind.Long).unrestricted(), IntegerStamp.create(64, -1, -1))); + } + + private static void testMulHigh(BinaryOp someMulHigh, long a, long b, int bits) { + long expectedResult = getExpectedValue(someMulHigh, a, b, bits); + assertEquals(IntegerStamp.create(bits, expectedResult, expectedResult), someMulHigh.foldStamp(IntegerStamp.create(bits, a, a), IntegerStamp.create(bits, b, b))); + } + + private static long getExpectedValue(BinaryOp someMulHigh, long a, long b, int bits) { + if (someMulHigh == IntegerStamp.OPS.getMulHigh()) { + return mulHigh(a, b, bits); + } else { + assertEquals(IntegerStamp.OPS.getUMulHigh(), someMulHigh); + return umulHigh(a, b, bits); + } + } + + private static long mulHigh(long a, long b, int bits) { + BigInteger valA = BigInteger.valueOf(a); + BigInteger valB = BigInteger.valueOf(b); + BigInteger result = valA.multiply(valB).shiftRight(bits); + if (bits == 32) { + return result.intValue(); + } else { + assertEquals(64, bits); + return result.longValue(); + } + } + + private static long umulHigh(long a, long b, int bits) { + Assert.assertTrue(bits == 32 || bits == 64); + BigInteger valA = BigInteger.valueOf(a); + if (valA.compareTo(BigInteger.valueOf(0)) < 0) { + valA = valA.add(BigInteger.ONE.shiftLeft(bits)); + } + BigInteger valB = BigInteger.valueOf(b); + if (valB.compareTo(BigInteger.valueOf(0)) < 0) { + valB = valB.add(BigInteger.ONE.shiftLeft(bits)); + } + + BigInteger result = valA.multiply(valB).shiftRight(bits); + if (bits == 32) { + return result.intValue(); + } else { + return result.longValue(); + } } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java Wed Aug 23 20:20:59 2017 +0000 @@ -466,6 +466,28 @@ AbstractMergeNode merge = (AbstractMergeNode) node; EndNode singleEnd = merge.forwardEndAt(0); + /* + * In some corner cases, the MergeNode already has PhiNodes. Since there is a single + * EndNode, each PhiNode can only have one input, and we can replace the PhiNode with + * this single input. + */ + for (PhiNode phi : merge.phis()) { + assert phi.inputs().count() == 1 : "input count must match end count"; + Node singlePhiInput = phi.inputs().first(); + + /* + * We do not have the orderID of the PhiNode anymore, so we need to search through + * the complete list of nodes to find a match. + */ + for (int i = 0; i < loopScope.createdNodes.length; i++) { + if (loopScope.createdNodes[i] == phi) { + loopScope.createdNodes[i] = singlePhiInput; + } + } + + phi.replaceAndDelete(singlePhiInput); + } + /* Nodes that would use this merge as the guard need to use the previous block. */ registerNode(loopScope, nodeOrderId, AbstractBeginNode.prevBegin(singleEnd), true, false); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,6 +25,7 @@ import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; import org.graalvm.compiler.core.common.type.ArithmeticOpTable; +import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Mul; import org.graalvm.compiler.core.common.type.Stamp; @@ -108,6 +109,27 @@ return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX); } else if (CodeUtil.isPowerOf2(i + 1)) { return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX); + } else { + int bitCount = Long.bitCount(i); + long highestBitValue = Long.highestOneBit(i); + if (bitCount == 2) { + // e.g., 0b1000_0010 + long lowerBitValue = i - highestBitValue; + assert highestBitValue > 0 && lowerBitValue > 0; + ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(highestBitValue))); + ValueNode right = lowerBitValue == 1 ? forX : new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(lowerBitValue))); + return AddNode.create(left, right); + } else { + // e.g., 0b1111_1101 + int shiftToRoundUpToPowerOf2 = CodeUtil.log2(highestBitValue) + 1; + long subValue = (1 << shiftToRoundUpToPowerOf2) - i; + if (CodeUtil.isPowerOf2(subValue) && shiftToRoundUpToPowerOf2 < ((IntegerStamp) stamp).getBits()) { + assert CodeUtil.log2(subValue) >= 1; + ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(shiftToRoundUpToPowerOf2)); + ValueNode right = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(subValue))); + return SubNode.create(left, right); + } + } } } else if (i < 0) { if (CodeUtil.isPowerOf2(-i)) { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java Wed Aug 23 20:20:59 2017 +0000 @@ -562,7 +562,7 @@ if (pred.getSuccessorCount() > 1) { assert pred.getEndNode() instanceof ControlSplitNode; ControlSplitNode controlSplit = (ControlSplitNode) pred.getEndNode(); - probability *= controlSplit.probability(block.getBeginNode()); + probability = multiplyProbabilities(probability, controlSplit.probability(block.getBeginNode())); } } else { probability = predecessors[0].probability; @@ -572,7 +572,7 @@ if (block.getBeginNode() instanceof LoopBeginNode) { LoopBeginNode loopBegin = (LoopBeginNode) block.getBeginNode(); - probability *= loopBegin.loopFrequency(); + probability = multiplyProbabilities(probability, loopBegin.loopFrequency()); } } if (probability < MIN_PROBABILITY) { @@ -755,4 +755,20 @@ public void setNodeToBlock(NodeMap nodeMap) { this.nodeToBlock = nodeMap; } + + /** + * Multiplies a and b and clamps the between {@link ControlFlowGraph#MIN_PROBABILITY} and + * {@link ControlFlowGraph#MAX_PROBABILITY}. + */ + public static double multiplyProbabilities(double a, double b) { + assert !Double.isNaN(a) && !Double.isNaN(b) && Double.isFinite(a) && Double.isFinite(b) : a + " " + b; + double r = a * b; + if (r > MAX_PROBABILITY) { + return MAX_PROBABILITY; + } + if (r < MIN_PROBABILITY) { + return MIN_PROBABILITY; + } + return r; + } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.processor/src/org/graalvm/compiler/options/processor/OptionProcessor.java Wed Aug 23 20:20:59 2017 +0000 @@ -22,9 +22,12 @@ */ package org.graalvm.compiler.options.processor; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -50,7 +53,9 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionDescriptor; @@ -117,22 +122,13 @@ return; } - String help = annotation.help(); - if (help.length() != 0) { - char firstChar = help.charAt(0); - if (!Character.isUpperCase(firstChar)) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element); - return; - } - } - String optionName = annotation.name(); if (optionName.equals("")) { optionName = fieldName; } if (!Character.isUpperCase(optionName.charAt(0))) { - processingEnv.getMessager().printMessage(Kind.ERROR, "Option name must start with capital letter", element); + processingEnv.getMessager().printMessage(Kind.ERROR, "Option name must start with an upper case letter", element); return; } @@ -154,6 +150,7 @@ String separator = ""; Set originatingElementsList = info.originatingElements; originatingElementsList.add(field); + PackageElement enclosingPackage = null; while (enclosing != null) { if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { @@ -164,13 +161,64 @@ originatingElementsList.add(enclosing); declaringClass = enclosing.getSimpleName() + separator + declaringClass; separator = "."; - } else { - assert enclosing.getKind() == ElementKind.PACKAGE; + } else if (enclosing.getKind() == ElementKind.PACKAGE) { + enclosingPackage = (PackageElement) enclosing; } enclosing = enclosing.getEnclosingElement(); } + if (enclosingPackage == null) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option field cannot be declared in the unnamed package", element); + return; + } + String[] helpValue = annotation.help(); + String help = ""; + String[] extraHelp = {}; - info.options.add(new OptionInfo(optionName, help, optionType, declaringClass, field)); + if (helpValue.length == 1) { + help = helpValue[0]; + if (help.startsWith("file:")) { + String path = help.substring("file:".length()); + Filer filer = processingEnv.getFiler(); + try { + FileObject file; + try { + file = filer.getResource(StandardLocation.SOURCE_PATH, enclosingPackage.getQualifiedName(), path); + } catch (IllegalArgumentException | IOException e) { + // Handle the case when a compiler doesn't support the SOURCE_PATH location + file = filer.getResource(StandardLocation.CLASS_OUTPUT, enclosingPackage.getQualifiedName(), path); + } + try (BufferedReader br = new BufferedReader(new InputStreamReader(file.openInputStream()))) { + help = br.readLine(); + if (help == null) { + help = ""; + } + String line = br.readLine(); + List lines = new ArrayList<>(); + while (line != null) { + lines.add(line); + line = br.readLine(); + } + extraHelp = lines.toArray(new String[lines.size()]); + } + } catch (IOException e) { + String msg = String.format("Error reading %s containing the help text for option field: %s", path, e); + processingEnv.getMessager().printMessage(Kind.ERROR, msg, element); + return; + } + } + } else if (helpValue.length > 1) { + help = helpValue[0]; + extraHelp = Arrays.copyOfRange(helpValue, 1, helpValue.length); + } + if (help.length() != 0) { + char firstChar = help.charAt(0); + if (!Character.isUpperCase(firstChar)) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with an upper case letter", element); + return; + } + } + + info.options.add(new OptionInfo(optionName, help, extraHelp, optionType, declaringClass, field)); } private void createFiles(OptionsInfo info) { @@ -200,11 +248,11 @@ String desc = OptionDescriptor.class.getSimpleName(); - int i = 0; Collections.sort(info.options); out.println(" @Override"); out.println(" public OptionDescriptor get(String value) {"); + out.println(" switch (value) {"); out.println(" // CheckStyle: stop line length check"); for (OptionInfo option : info.options) { String name = option.name; @@ -214,41 +262,52 @@ } else { optionField = option.declaringClass + "." + option.field.getSimpleName(); } + out.println(" case \"" + name + "\": {"); String type = option.type; String help = option.help; + String[] extraHelp = option.extraHelp; String declaringClass = option.declaringClass; Name fieldName = option.field.getSimpleName(); - out.println(" if (value.equals(\"" + name + "\")) {"); - out.printf(" return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionField); + out.printf(" return " + desc + ".create(\n"); + out.printf(" /*name*/ \"%s\",\n", name); + out.printf(" /*type*/ %s.class,\n", type); + out.printf(" /*help*/ \"%s\",\n", help); + if (extraHelp.length != 0) { + out.printf(" /*extraHelp*/ new String[] {\n"); + for (String line : extraHelp) { + out.printf(" \"%s\",\n", line.replace("\\", "\\\\").replace("\"", "\\\"")); + } + out.printf(" },\n"); + } + out.printf(" /*declaringClass*/ %s.class,\n", declaringClass); + out.printf(" /*fieldName*/ \"%s\",\n", fieldName); + out.printf(" /*option*/ %s);\n", optionField); out.println(" }"); } out.println(" // CheckStyle: resume line length check"); + out.println(" }"); out.println(" return null;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public Iterator<" + desc + "> iterator() {"); - out.println(" // CheckStyle: stop line length check"); - out.println(" List<" + desc + "> options = Arrays.asList("); - for (OptionInfo option : info.options) { - String optionField; - if (option.field.getModifiers().contains(Modifier.PRIVATE)) { - throw new InternalError(); - } else { - optionField = option.declaringClass + "." + option.field.getSimpleName(); - } - String name = option.name; - String type = option.type; - String help = option.help; - String declaringClass = option.declaringClass; - Name fieldName = option.field.getSimpleName(); - String comma = i == info.options.size() - 1 ? "" : ","; - out.printf(" %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionField, comma); - i++; + out.println(" return new Iterator() {"); + out.println(" int i = 0;"); + out.println(" @Override"); + out.println(" public boolean hasNext() {"); + out.println(" return i < " + info.options.size() + ";"); + out.println(" }"); + out.println(" @Override"); + out.println(" public OptionDescriptor next() {"); + out.println(" switch (i++) {"); + for (int i = 0; i < info.options.size(); i++) { + OptionInfo option = info.options.get(i); + out.println(" case " + i + ": return get(\"" + option.name + "\");"); } - out.println(" );"); - out.println(" // CheckStyle: resume line length check"); - out.println(" return options.iterator();"); + out.println(" }"); + out.println(" throw new NoSuchElementException();"); + out.println(" }"); + out.println(" };"); out.println(" }"); out.println("}"); } @@ -274,13 +333,15 @@ final String name; final String help; + final String[] extraHelp; final String type; final String declaringClass; final VariableElement field; - OptionInfo(String name, String help, String type, String declaringClass, VariableElement field) { + OptionInfo(String name, String help, String[] extraHelp, String type, String declaringClass, VariableElement field) { this.name = name; this.help = help; + this.extraHelp = extraHelp; this.type = type; this.declaringClass = declaringClass; this.field = field; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/EnumOptionKey.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/EnumOptionKey.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/EnumOptionKey.java Wed Aug 23 20:20:59 2017 +0000 @@ -28,27 +28,10 @@ public class EnumOptionKey> extends OptionKey { final Class enumClass; - final ValueHelp valueHelp; - - /** - * Provides help text for enum values. - */ - public interface ValueHelp> { - /** - * Gets help text for the enum {@code value} that includes the name of the value. If - * {@code null} is returned, {@code value.toString()} is used. - */ - String getHelp(Object value); - } - - public EnumOptionKey(T value) { - this(value, null); - } @SuppressWarnings("unchecked") - public EnumOptionKey(T value, ValueHelp help) { + public EnumOptionKey(T value) { super(value); - this.valueHelp = help; if (value == null) { throw new IllegalArgumentException("Value must not be null"); } @@ -62,10 +45,6 @@ return EnumSet.allOf(enumClass); } - public ValueHelp getValueHelp() { - return valueHelp; - } - Object valueOf(String name) { try { return Enum.valueOf(enumClass, name); diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/Option.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/Option.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/Option.java Wed Aug 23 20:20:59 2017 +0000 @@ -38,10 +38,20 @@ public @interface Option { /** - * Gets a help message for the option. New lines can be embedded in the message with - * {@code "%n"}. + * Gets a help message for the option. + *

+ * The first element of the array is the short help message. This part of the help message is + * subject to line wrapping when printed. + *

+ * The remaining elements contain a more detailed expansion of the help message and will be + * printed as is in a left-aligned block (i.e. leading spaces will be preserved). + *

+ * If there is only one element and it starts with {@code "file:"}, then the help message + * is located in a file located by resolving {@code } against the location of the package + * in which the option is declared. The first line in the file is the short help message as + * described above. The remaining lines are the help message expansion. */ - String help(); + String[] help(); /** * The name of the option. By default, the name of the annotated field should be used. diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionDescriptor.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionDescriptor.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionDescriptor.java Wed Aug 23 20:20:59 2017 +0000 @@ -22,6 +22,10 @@ */ package org.graalvm.compiler.options; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + /** * Describes the attributes of a static field {@linkplain Option option} and provides access to its * {@linkplain OptionKey value}. @@ -31,25 +35,34 @@ protected final String name; protected final Class type; protected final String help; + protected final List extraHelp; protected final OptionKey optionKey; protected final Class declaringClass; protected final String fieldName; + private static final String[] NO_EXTRA_HELP = {}; + public static OptionDescriptor create(String name, Class type, String help, Class declaringClass, String fieldName, OptionKey option) { + return create(name, type, help, NO_EXTRA_HELP, declaringClass, fieldName, option); + } + + public static OptionDescriptor create(String name, Class type, String help, String[] extraHelp, Class declaringClass, String fieldName, OptionKey option) { assert option != null : declaringClass + "." + fieldName; OptionDescriptor result = option.getDescriptor(); if (result == null) { - result = new OptionDescriptor(name, type, help, declaringClass, fieldName, option); + List extraHelpList = extraHelp == null || extraHelp.length == 0 ? Collections.emptyList() : Collections.unmodifiableList(Arrays.asList(extraHelp)); + result = new OptionDescriptor(name, type, help, extraHelpList, declaringClass, fieldName, option); option.setDescriptor(result); } assert result.name.equals(name) && result.type == type && result.declaringClass == declaringClass && result.fieldName.equals(fieldName) && result.optionKey == option; return result; } - private OptionDescriptor(String name, Class type, String help, Class declaringClass, String fieldName, OptionKey optionKey) { + private OptionDescriptor(String name, Class type, String help, List extraHelp, Class declaringClass, String fieldName, OptionKey optionKey) { this.name = name; this.type = type; this.help = help; + this.extraHelp = extraHelp; this.optionKey = optionKey; this.declaringClass = declaringClass; this.fieldName = fieldName; @@ -65,13 +78,24 @@ } /** - * Gets a descriptive help message for the option. + * Gets a descriptive help message for the option. This message should be self contained without + * relying on {@link #getExtraHelp() extra help lines}. + * + * @see Option#help() */ public String getHelp() { return help; } /** + * Gets extra lines of help text. These lines should not be subject to any line wrapping or + * formatting apart from indentation. + */ + public List getExtraHelp() { + return extraHelp; + } + + /** * Gets the name of the option. It's up to the client of this object how to use the name to get * a user specified value for the option from the environment. */ diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java Wed Aug 23 20:20:59 2017 +0000 @@ -24,15 +24,12 @@ import java.io.PrintStream; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; -import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; -import org.graalvm.compiler.options.EnumOptionKey.ValueHelp; import org.graalvm.util.EconomicMap; import org.graalvm.util.Equivalence; import org.graalvm.util.UnmodifiableEconomicMap; @@ -165,10 +162,9 @@ * @return {@code text} broken into lines */ private static List wrap(String text, int width) { - List lines = Collections.singletonList(text); + List lines = new ArrayList<>(); if (text.length() > width) { String[] chunks = text.split("\\s+"); - lines = new ArrayList<>(); StringBuilder line = new StringBuilder(); for (String chunk : chunks) { if (line.length() + chunk.length() > width) { @@ -178,22 +174,13 @@ if (line.length() != 0) { line.append(' '); } - String[] embeddedLines = chunk.split("%n", -2); - if (embeddedLines.length == 1) { - line.append(chunk); - } else { - for (int i = 0; i < embeddedLines.length; i++) { - line.append(embeddedLines[i]); - if (i < embeddedLines.length - 1) { - lines.add(line.toString()); - line.setLength(0); - } - } - } + line.append(chunk); } if (line.length() != 0) { lines.add(line.toString()); } + } else { + lines.add(text); } return lines; } @@ -222,24 +209,7 @@ if (value instanceof String) { value = '"' + String.valueOf(value) + '"'; } - String help = desc.getHelp(); - if (desc.getOptionKey() instanceof EnumOptionKey) { - EnumOptionKey eoption = (EnumOptionKey) desc.getOptionKey(); - EnumSet evalues = eoption.getAllValues(); - String evaluesString = evalues.toString(); - ValueHelp valueHelp = eoption.getValueHelp(); - if (help.length() > 0 && !help.endsWith(".")) { - help += "."; - } - if (valueHelp == null) { - help += " Valid values are: " + evaluesString.substring(1, evaluesString.length() - 1); - } else { - for (Object o : evalues) { - String vhelp = valueHelp.getHelp(o); - help += "%n" + (vhelp == null ? o : vhelp); - } - } - } + String name = namePrefix + e.getKey(); String assign = containsKey(desc.optionKey) ? ":=" : "="; String typeName = desc.getOptionKey() instanceof EnumOptionKey ? "String" : desc.getType().getSimpleName(); @@ -252,11 +222,16 @@ out.printf("%s[%s]%n", linePrefix, typeName); } + List helpLines; + String help = desc.getHelp(); if (help.length() != 0) { - List helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT); - for (int i = 0; i < helpLines.size(); i++) { - out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", helpLines.get(i)); - } + helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT); + helpLines.addAll(desc.getExtraHelp()); + } else { + helpLines = desc.getExtraHelp(); + } + for (String line : helpLines) { + out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line); } } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java Wed Aug 23 20:20:59 2017 +0000 @@ -30,6 +30,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import org.graalvm.compiler.api.replacements.MethodSubstitution; @@ -351,7 +352,7 @@ } } - updateSourcePositions(invoke, inlineGraph, duplicates); + updateSourcePositions(invoke, inlineGraph, duplicates, !Objects.equals(inlineGraph.method(), inlineeMethod)); if (stateAfter != null) { processFrameStates(invoke, inlineGraph, duplicates, stateAtExceptionEdge, returnNodes.size() > 1); int callerLockDepth = stateAfter.nestedLockDepth(); @@ -569,14 +570,14 @@ } @SuppressWarnings("try") - private static void updateSourcePositions(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap duplicates) { + private static void updateSourcePositions(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap duplicates, boolean isSubstitution) { if (inlineGraph.mayHaveNodeSourcePosition() && invoke.stateAfter() != null) { if (invoke.asNode().getNodeSourcePosition() == null) { // Temporarily ignore the assert below. return; } - JavaConstant constantReceiver = invoke.getInvokeKind().hasReceiver() ? invoke.getReceiver().asJavaConstant() : null; + JavaConstant constantReceiver = invoke.getInvokeKind().hasReceiver() && !isSubstitution ? invoke.getReceiver().asJavaConstant() : null; NodeSourcePosition invokePos = invoke.asNode().getNodeSourcePosition(); assert invokePos != null : "missing source information"; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/policy/AbstractInliningPolicy.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/policy/AbstractInliningPolicy.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/policy/AbstractInliningPolicy.java Wed Aug 23 20:20:59 2017 +0000 @@ -75,10 +75,11 @@ } private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i), info.invoke().bci())) { - return false; - } + if (!onlyIntrinsics(replacements, info)) { + return false; + } + if (!info.shouldInline()) { + return false; } return true; } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/FixedNodeProbabilityCache.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/FixedNodeProbabilityCache.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/FixedNodeProbabilityCache.java Wed Aug 23 20:20:59 2017 +0000 @@ -39,6 +39,8 @@ import org.graalvm.util.EconomicMap; import org.graalvm.util.Equivalence; +import static org.graalvm.compiler.nodes.cfg.ControlFlowGraph.multiplyProbabilities; + /** * Compute probabilities for fixed nodes on the fly and cache them at {@link AbstractBeginNode}s. */ @@ -106,7 +108,7 @@ } } else { ControlSplitNode split = (ControlSplitNode) current.predecessor(); - probability = split.probability((AbstractBeginNode) current) * applyAsDouble(split); + probability = multiplyProbabilities(split.probability((AbstractBeginNode) current), applyAsDouble(split)); } assert !Double.isNaN(probability) && !Double.isInfinite(probability) : current + " " + probability; cache.put(current, probability); @@ -125,7 +127,7 @@ result += applyAsDouble(endNode); } if (current instanceof LoopBeginNode) { - result *= ((LoopBeginNode) current).loopFrequency(); + result = multiplyProbabilities(result, ((LoopBeginNode) current).loopFrequency()); } return result; } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java Wed Aug 23 20:20:59 2017 +0000 @@ -366,7 +366,7 @@ return ((Class) obj).getName(); } if (obj instanceof ResolvedJavaType) { - return ((ResolvedJavaType) obj).getName(); + return ((ResolvedJavaType) obj).toJavaName(); } return null; } @@ -403,7 +403,7 @@ @Override public String fieldTypeName(ResolvedJavaField field) { - return field.getType().getName(); + return field.getType().toJavaName(); } @Override diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java Wed Aug 23 20:20:59 2017 +0000 @@ -31,6 +31,8 @@ import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN; import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier; +import java.util.Arrays; + import org.graalvm.compiler.bytecode.BytecodeProvider; import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMode; import org.graalvm.compiler.nodes.ValueNode; @@ -44,6 +46,7 @@ import org.graalvm.compiler.nodes.java.AtomicReadAndWriteNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; +import org.graalvm.compiler.replacements.ArraysSubstitutions; import org.graalvm.compiler.replacements.IntegerSubstitutions; import org.graalvm.compiler.replacements.LongSubstitutions; import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafeGetPlugin; @@ -73,6 +76,7 @@ registerUnsafePlugins(invocationPlugins, replacementsBytecodeProvider); registerStringPlugins(invocationPlugins, arch, replacementsBytecodeProvider); registerMathPlugins(invocationPlugins, arch, arithmeticStubs, replacementsBytecodeProvider); + registerArraysEqualsPlugins(invocationPlugins, replacementsBytecodeProvider); } }); } @@ -229,4 +233,10 @@ r.registerOptional4("put" + kind.name() + "Unaligned", Receiver.class, Object.class, long.class, javaClass, new UnsafePutPlugin(kind, false)); } } + + private static void registerArraysEqualsPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) { + Registration r = new Registration(plugins, Arrays.class, bytecodeProvider); + r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class); + r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class); + } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ArraysSubstitutionsTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ArraysSubstitutionsTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ArraysSubstitutionsTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -232,68 +232,6 @@ } @Test - public void testEqualsFloat() { - Object[] args1 = new Object[N]; - Object[] args2 = new Object[N]; - int n = 0; - - // equal arrays - for (int i = 0; i < N / 2; i++, n++) { - args1[n] = new float[i]; - args2[n] = new float[i]; - } - - // non-equal arrays - for (int i = 0; i < N / 2; i++, n++) { - float[] a2 = new float[i]; - if (i > 0) { - a2[i - 1] = 1; - } - args1[n] = new float[i]; - args2[n] = a2; - } - - Class[] parameterTypes = new Class[]{float[].class, float[].class}; - testSubstitution("arraysEqualsFloat", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2); - } - - @SuppressWarnings("all") - public static boolean arraysEqualsFloat(float[] a, float[] b) { - return Arrays.equals(a, b); - } - - @Test - public void testEqualsDouble() { - Object[] args1 = new Object[N]; - Object[] args2 = new Object[N]; - int n = 0; - - // equal arrays - for (int i = 0; i < N / 2; i++, n++) { - args1[n] = new double[i]; - args2[n] = new double[i]; - } - - // non-equal arrays - for (int i = 0; i < N / 2; i++, n++) { - double[] a2 = new double[i]; - if (i > 0) { - a2[i - 1] = 1; - } - args1[n] = new double[i]; - args2[n] = a2; - } - - Class[] parameterTypes = new Class[]{double[].class, double[].class}; - testSubstitution("arraysEqualsDouble", ArrayEqualsNode.class, Arrays.class, "equals", parameterTypes, false, args1, args2); - } - - @SuppressWarnings("all") - public static boolean arraysEqualsDouble(double[] a, double[] b) { - return Arrays.equals(a, b); - } - - @Test public void testEqualsNodeGVN() { test("testEqualsNodeGVNSnippet", true); } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnExceptionTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnExceptionTest.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnExceptionTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -24,16 +24,25 @@ import java.util.Random; +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.core.phases.HighTier; import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; +import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.common.AbstractInliningPhase; import org.graalvm.compiler.test.ExportingClassLoader; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.ResolvedJavaMethod; /** @@ -90,6 +99,61 @@ return "SUCCESS"; } + @Test + public void test3() { + Assume.assumeTrue("Only works on jdk8 right now", Java8OrEarlier); + ResolvedJavaMethod method = getResolvedJavaMethod("test3Snippet"); + + for (int i = 0; i < 2; i++) { + Result actual; + boolean expectedCompiledCode = (method.getProfilingInfo().getDeoptimizationCount(DeoptimizationReason.NotCompiledExceptionHandler) != 0); + InstalledCode code = getCode(method, null, false, true, new OptionValues(getInitialOptions(), HighTier.Options.Inline, false)); + assertTrue(code.isValid()); + + try { + actual = new Result(code.executeVarargs(false), null); + } catch (Exception e) { + actual = new Result(null, e); + } + + assertTrue(i > 0 == expectedCompiledCode, "expect compiled code to stay around after the first iteration"); + assertEquals(new Result(expectedCompiledCode, null), actual); + assertTrue(expectedCompiledCode == code.isValid()); + } + } + + @Override + protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { + if (method.getName().equals("throwException")) { + if (b.getMethod().getProfilingInfo().getDeoptimizationCount(DeoptimizationReason.NotCompiledExceptionHandler) != 0) { + return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION; + } else { + return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_NO_EXCEPTION; + } + } + return super.bytecodeParserShouldInlineInvoke(b, method, args); + } + + private static void throwException() throws Exception { + throw new Exception(); + } + + static int footprint; + + public static boolean test3Snippet(boolean rethrowException) throws Exception { + try { + footprint = 1; + throwException(); + } catch (Exception e) { + footprint = 2; + if (rethrowException) { + throw e; + } + } + + return GraalDirectives.inCompiledCode(); + } + public static class MyClassLoader extends ExportingClassLoader { @Override protected Class findClass(String className) throws ClassNotFoundException { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/FloatArraysEqualsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/FloatArraysEqualsTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.compiler.replacements.test; + +import java.util.Arrays; + +import org.graalvm.compiler.code.CompilationResult; +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; +import org.junit.Test; + +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public class FloatArraysEqualsTest extends GraalCompilerTest { + + public static boolean testFloatArraySnippet(float[] a, float[] b) { + return Arrays.equals(a, b); + } + + private void testEqualInFloatArray(int arraySize, int index, float f1, float f2) { + if (arraySize > 0 && index >= 0 && index < arraySize) { + float[] a1 = new float[arraySize]; + float[] a2 = new float[arraySize]; + a1[index] = f1; + a2[index] = f2; + test("testFloatArraySnippet", a1, a2); + } + } + + public static final int FLOAT_TAIL = 1; + public static final int FLOAT_8BYTE_ALIGNED = 2; + public static final int FLOAT_8BYTE_UNALIGNED = 3; + public static final int FLOAT_VECTOR_ALIGNED = 8; + public static final int FLOAT_VECTOR_UNALIGNED = 9; + + @Test + public void testFloatArray() { + for (int size : new int[]{FLOAT_TAIL, FLOAT_8BYTE_ALIGNED, FLOAT_8BYTE_UNALIGNED, FLOAT_VECTOR_ALIGNED, FLOAT_VECTOR_UNALIGNED}) { + for (int index : new int[]{0, size - 1}) { + testEqualInFloatArray(size, index, 1024, 1024); + testEqualInFloatArray(size, index, 0.0f, -0.0f); + testEqualInFloatArray(size, index, Float.intBitsToFloat(0x7fc00000), Float.intBitsToFloat(0x7fc00000)); + testEqualInFloatArray(size, index, Float.intBitsToFloat(0x7fc00000), Float.intBitsToFloat(0x7f800001)); + testEqualInFloatArray(size, index, Float.intBitsToFloat(0x7fc00000), 1024); + } + } + } + + public static boolean testDoubleArraySnippet(double[] a, double[] b) { + return Arrays.equals(a, b); + } + + public static final int DOUBLE_8BYTE_ALIGNED = 1; + public static final int DOUBLE_VECTOR_ALIGNED = 4; + public static final int DOUBLE_VECTOR_UNALIGNED = 5; + + private void testEqualInDoubleArray(int arraySize, int index, double d1, double d2) { + if (arraySize > 0 && index >= 0 && index < arraySize) { + double[] a1 = new double[arraySize]; + double[] a2 = new double[arraySize]; + a1[index] = d1; + a2[index] = d2; + test("testDoubleArraySnippet", a1, a2); + } + } + + @Test + public void testDoubleArrayOrdinary() { + for (int size : new int[]{DOUBLE_8BYTE_ALIGNED, DOUBLE_VECTOR_ALIGNED, DOUBLE_VECTOR_UNALIGNED}) { + for (int index : new int[]{0, size - 1}) { + testEqualInDoubleArray(size, index, 1024, 1024); + testEqualInDoubleArray(size, index, 0.0d, -0.0d); + testEqualInDoubleArray(size, index, Double.longBitsToDouble(0x7ff8000000000000L), Double.longBitsToDouble(0x7ff8000000000000L)); + testEqualInDoubleArray(size, index, Double.longBitsToDouble(0x7ff8000000000000L), Double.longBitsToDouble(0x7ff0000000000001L)); + testEqualInDoubleArray(size, index, Double.longBitsToDouble(0x7ff8000000000000L), 1024); + } + } + } + + public static boolean testFloatArrayWithPEASnippet0() { + return Arrays.equals(new float[]{0.0f}, new float[]{-0.0f}); + } + + public static boolean testFloatArrayWithPEASnippet1() { + return Arrays.equals(new float[]{Float.intBitsToFloat(0x7fc00000)}, new float[]{Float.intBitsToFloat(0x7fc00000)}); + } + + public static boolean testFloatArrayWithPEASnippet2() { + return Arrays.equals(new float[]{Float.intBitsToFloat(0x7fc00000)}, new float[]{Float.intBitsToFloat(0x7f800001)}); + + } + + @Test + public void testFloatArrayWithPEA() { + test("testFloatArrayWithPEASnippet0"); + test("testFloatArrayWithPEASnippet1"); + test("testFloatArrayWithPEASnippet2"); + } + + public static boolean testDoubleArrayWithPEASnippet0() { + return Arrays.equals(new double[]{0.0d}, new double[]{-0.0d}); + } + + public static boolean testDoubleArrayWithPEASnippet1() { + return Arrays.equals(new double[]{Double.longBitsToDouble(0x7ff8000000000000L)}, new double[]{Double.longBitsToDouble(0x7ff8000000000000L)}); + } + + public static boolean testDoubleArrayWithPEASnippet2() { + return Arrays.equals(new double[]{Double.longBitsToDouble(0x7ff8000000000000L)}, new double[]{Double.longBitsToDouble(0x7ff0000000000001L)}); + } + + @Test + public void testDoubleArrayWithPEA() { + test("testDoubleArrayWithPEASnippet0"); + test("testDoubleArrayWithPEASnippet1"); + test("testDoubleArrayWithPEASnippet2"); + } + + public static final float[] FLOAT_ARRAY1 = new float[]{0.0f}; + public static final float[] FLOAT_ARRAY2 = new float[]{-0.0f}; + public static final float[] FLOAT_ARRAY3 = new float[]{Float.intBitsToFloat(0x7fc00000)}; + public static final float[] FLOAT_ARRAY4 = new float[]{Float.intBitsToFloat(0x7f800001)}; + + public static final double[] DOUBLE_ARRAY1 = new double[]{0.0d}; + public static final double[] DOUBLE_ARRAY2 = new double[]{-0.0d}; + public static final double[] DOUBLE_ARRAY3 = new double[]{Double.longBitsToDouble(0x7ff8000000000000L)}; + public static final double[] DOUBLE_ARRAY4 = new double[]{Double.longBitsToDouble(0x7ff0000000000001L)}; + + public static boolean testStableFloatArraySnippet0() { + return Arrays.equals(FLOAT_ARRAY1, FLOAT_ARRAY2); + } + + public static boolean testStableFloatArraySnippet1() { + return Arrays.equals(FLOAT_ARRAY1, FLOAT_ARRAY2); + } + + public static boolean testStableDoubleArraySnippet0() { + return Arrays.equals(DOUBLE_ARRAY1, DOUBLE_ARRAY2); + } + + public static boolean testStableDoubleArraySnippet1() { + return Arrays.equals(DOUBLE_ARRAY3, DOUBLE_ARRAY4); + } + + public void testStableArray(String methodName) { + ResolvedJavaMethod method = getResolvedJavaMethod(methodName); + Result expected = executeExpected(method, null); + + StructuredGraph graph = parseEager(method, AllowAssumptions.YES); + + for (ConstantNode constantNode : graph.getNodes().filter(ConstantNode.class).snapshot()) { + if (getConstantReflection().readArrayLength(constantNode.asJavaConstant()) != null) { + ConstantNode newConstantNode = ConstantNode.forConstant(constantNode.asJavaConstant(), 1, true, getMetaAccess()); + newConstantNode = graph.unique(newConstantNode); + constantNode.replaceAndDelete(newConstantNode); + } + } + + CompilationResult result = compile(method, graph); + InstalledCode code = addMethod(graph.getDebug(), method, result); + + Result actual; + + try { + actual = new Result(code.executeVarargs(), null); + } catch (Exception e) { + actual = new Result(null, e); + } + + assertEquals(expected, actual); + } + + @Test + public void testStableArray() { + testStableArray("testStableFloatArraySnippet0"); + testStableArray("testStableFloatArraySnippet1"); + testStableArray("testStableDoubleArraySnippet0"); + testStableArray("testStableDoubleArraySnippet1"); + } + +} diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/UnsafeBooleanAccessTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/UnsafeBooleanAccessTest.java Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017, 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 org.graalvm.compiler.replacements.test; + +import java.lang.reflect.Field; + +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.junit.Test; + +public class UnsafeBooleanAccessTest extends GraalCompilerTest { + + private static short onHeapMemory; + + private static final Object onHeapMemoryBase; + private static final long onHeapMemoryOffset; + + static { + try { + Field staticField = UnsafeBooleanAccessTest.class.getDeclaredField("onHeapMemory"); + onHeapMemoryBase = UNSAFE.staticFieldBase(staticField); + onHeapMemoryOffset = UNSAFE.staticFieldOffset(staticField); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean testGetBooleanSnippet() { + UNSAFE.putShort(onHeapMemoryBase, onHeapMemoryOffset, (short) 0x0204); + return UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset); + } + + @Test + public void testGetBoolean() { + test("testGetBooleanSnippet"); + } + + public static short testPutBooleanSnippet() { + UNSAFE.putShort(onHeapMemoryBase, onHeapMemoryOffset, (short) 0x0204); + boolean bool = UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset); + UNSAFE.putBoolean(onHeapMemoryBase, onHeapMemoryOffset, bool); + return onHeapMemory; + } + + @Test + public void testPutBoolean() { + test("testPutBooleanSnippet"); + } + + public static boolean testAndBooleanSnippet() { + UNSAFE.putShort(onHeapMemoryBase, onHeapMemoryOffset, (short) 0x0204); + boolean bool0 = UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset); + boolean bool1 = UNSAFE.getBoolean(onHeapMemoryBase, onHeapMemoryOffset + 1); + return bool0 & bool1; + } + + @Test + public void testAndBoolean() { + test("testAndBooleanSnippet"); + } + +} diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java Wed Aug 23 20:20:59 2017 +0000 @@ -59,8 +59,10 @@ import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; +import org.graalvm.compiler.nodes.calc.ConditionalNode; import org.graalvm.compiler.nodes.calc.IntegerBelowNode; import org.graalvm.compiler.nodes.calc.IntegerConvertNode; +import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; import org.graalvm.compiler.nodes.calc.IsNullNode; import org.graalvm.compiler.nodes.calc.LeftShiftNode; import org.graalvm.compiler.nodes.calc.NarrowNode; @@ -577,7 +579,7 @@ } else { memoryRead.setGuard(guard); } - ValueNode readValue = implicitLoadConvert(graph, readKind, memoryRead, compressible); + ValueNode readValue = performBooleanCoercionIfNecessary(implicitLoadConvert(graph, readKind, memoryRead, compressible), readKind); load.replaceAtUsages(readValue); return memoryRead; } @@ -592,11 +594,20 @@ // An unsafe read must not float otherwise it may float above // a test guaranteeing the read is safe. memoryRead.setForceFixed(true); - ValueNode readValue = implicitLoadConvert(graph, readKind, memoryRead, false); + ValueNode readValue = performBooleanCoercionIfNecessary(implicitLoadConvert(graph, readKind, memoryRead, false), readKind); load.replaceAtUsages(readValue); graph.replaceFixedWithFixed(load, memoryRead); } + private static ValueNode performBooleanCoercionIfNecessary(ValueNode readValue, JavaKind readKind) { + if (readKind == JavaKind.Boolean) { + StructuredGraph graph = readValue.graph(); + IntegerEqualsNode eq = graph.addOrUnique(new IntegerEqualsNode(readValue, ConstantNode.forInt(0, graph))); + return graph.addOrUnique(new ConditionalNode(eq, ConstantNode.forBoolean(false, graph), ConstantNode.forBoolean(true, graph))); + } + return readValue; + } + protected void lowerUnsafeStoreNode(RawStoreNode store) { StructuredGraph graph = store.graph(); boolean compressible = store.value().getStackKind() == JavaKind.Object; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java Wed Aug 23 20:20:59 2017 +0000 @@ -189,9 +189,7 @@ r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", short[].class, short[].class); r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", char[].class, char[].class); r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", int[].class, int[].class); - r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class); r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", long[].class, long[].class); - r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class); } private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) { diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java Wed Aug 23 20:20:59 2017 +0000 @@ -95,11 +95,16 @@ return length; } + private static boolean isNaNFloat(JavaConstant constant) { + JavaKind kind = constant.getJavaKind(); + return (kind == JavaKind.Float && Float.isNaN(constant.asFloat())) || (kind == JavaKind.Double && Double.isNaN(constant.asDouble())); + } + private static boolean arrayEquals(ConstantReflectionProvider constantReflection, JavaConstant a, JavaConstant b, int len) { for (int i = 0; i < len; i++) { JavaConstant aElem = constantReflection.readArrayElement(a, i); JavaConstant bElem = constantReflection.readArrayElement(b, i); - if (!constantReflection.constantEquals(aElem, bElem)) { + if (!constantReflection.constantEquals(aElem, bElem) && !(isNaNFloat(aElem) && isNaNFloat(bElem))) { return false; } } @@ -145,8 +150,28 @@ ValueNode entry1 = tool.getEntry(virtual1, i); ValueNode entry2 = tool.getEntry(virtual2, i); if (entry1 != entry2) { - // the contents might be different - allEqual = false; + if (entry1 instanceof ConstantNode && entry2 instanceof ConstantNode) { + // Float NaN constants are different constant nodes but treated as + // equal in Arrays.equals([F[F) or Arrays.equals([D[D). + if (entry1.getStackKind() == JavaKind.Float && entry2.getStackKind() == JavaKind.Float) { + float value1 = ((JavaConstant) ((ConstantNode) entry1).asConstant()).asFloat(); + float value2 = ((JavaConstant) ((ConstantNode) entry2).asConstant()).asFloat(); + if (Float.floatToIntBits(value1) != Float.floatToIntBits(value2)) { + allEqual = false; + } + } else if (entry1.getStackKind() == JavaKind.Double && entry2.getStackKind() == JavaKind.Double) { + double value1 = ((JavaConstant) ((ConstantNode) entry1).asConstant()).asDouble(); + double value2 = ((JavaConstant) ((ConstantNode) entry2).asConstant()).asDouble(); + if (Double.doubleToLongBits(value1) != Double.doubleToLongBits(value2)) { + allEqual = false; + } + } else { + allEqual = false; + } + } else { + // the contents might be different + allEqual = false; + } } if (entry1.stamp().alwaysDistinct(entry2.stamp())) { // the contents are different diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ExplodeLoopNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ExplodeLoopNode.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ExplodeLoopNode.java Wed Aug 23 20:20:59 2017 +0000 @@ -57,7 +57,7 @@ for (Node n : currentNext.cfgSuccessors()) { succs.add(n); } - if (succs.size() == 1) { + if (succs.size() == 1 && succs.get(0) != currentNext) { currentNext = succs.get(0); } else { return null; diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,69 +25,28 @@ import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; -import java.util.function.BiFunction; - -import org.graalvm.compiler.core.common.type.IntegerStamp; -import org.graalvm.compiler.core.common.type.Stamp; -import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.MulHigh; import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.calc.BinaryNode; -import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; +import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; -import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.PrimitiveConstant; import jdk.vm.ci.meta.Value; @NodeInfo(shortName = "*H", cycles = CYCLES_2, size = SIZE_2) -public final class IntegerMulHighNode extends BinaryNode implements ArithmeticLIRLowerable { +public final class IntegerMulHighNode extends BinaryArithmeticNode implements Canonicalizable.BinaryCommutative { public static final NodeClass TYPE = NodeClass.create(IntegerMulHighNode.class); public IntegerMulHighNode(ValueNode x, ValueNode y) { - this((IntegerStamp) x.stamp().unrestricted(), x, y); - } - - public IntegerMulHighNode(IntegerStamp stamp, ValueNode x, ValueNode y) { - super(TYPE, stamp, x, y); - } - - /** - * Determines the minimum and maximum result of this node for the given inputs and returns the - * result of the given BiFunction on the minimum and maximum values. - */ - private T processExtremes(Stamp forX, Stamp forY, BiFunction op) { - IntegerStamp xStamp = (IntegerStamp) forX; - IntegerStamp yStamp = (IntegerStamp) forY; - - JavaKind kind = getStackKind(); - assert kind == JavaKind.Int || kind == JavaKind.Long; - long[] xExtremes = {xStamp.lowerBound(), xStamp.upperBound()}; - long[] yExtremes = {yStamp.lowerBound(), yStamp.upperBound()}; - long min = Long.MAX_VALUE; - long max = Long.MIN_VALUE; - for (long a : xExtremes) { - for (long b : yExtremes) { - long result = kind == JavaKind.Int ? multiplyHigh((int) a, (int) b) : multiplyHigh(a, b); - min = Math.min(min, result); - max = Math.max(max, result); - } - } - return op.apply(min, max); - } - - @Override - public Stamp foldStamp(Stamp stampX, Stamp stampY) { - return processExtremes(stampX, stampY, (min, max) -> StampFactory.forInteger(getStackKind(), min, max)); - } - - @SuppressWarnings("cast") - @Override - public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - return processExtremes(forX.stamp(), forY.stamp(), (min, max) -> min == (long) max ? ConstantNode.forIntegerKind(getStackKind(), min) : this); + super(TYPE, ArithmeticOpTable::getMulHigh, x, y); } @Override @@ -97,29 +56,35 @@ nodeValueMap.setResult(this, gen.emitMulHigh(a, b)); } - public static int multiplyHigh(int x, int y) { - long r = (long) x * (long) y; - return (int) (r >> 32); + @Override + public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + ValueNode ret = super.canonical(tool, forX, forY); + if (ret != this) { + return ret; + } + + if (forX.isConstant() && !forY.isConstant()) { + // we try to swap and canonicalize + ValueNode improvement = canonical(tool, forY, forX); + if (improvement != this) { + return improvement; + } + // if this fails we only swap + return new IntegerMulHighNode(forY, forX); + } + return canonical(this, forY); } - public static long multiplyHigh(long x, long y) { - // Checkstyle: stop - long x0, y0, z0; - long x1, y1, z1, z2, t; - // Checkstyle: resume - - x0 = x & 0xFFFFFFFFL; - x1 = x >> 32; - - y0 = y & 0xFFFFFFFFL; - y1 = y >> 32; - - z0 = x0 * y0; - t = x1 * y0 + (z0 >>> 32); - z1 = t & 0xFFFFFFFFL; - z2 = t >> 32; - z1 += x0 * y1; - - return x1 * y1 + z2 + (z1 >> 32); + private static ValueNode canonical(IntegerMulHighNode self, ValueNode forY) { + if (forY.isConstant()) { + Constant c = forY.asConstant(); + if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) { + long i = ((PrimitiveConstant) c).asLong(); + if (i == 0 || i == 1) { + return ConstantNode.forIntegerStamp(self.stamp(), 0); + } + } + } + return self; } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java Wed Aug 23 13:14:20 2017 -0700 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java Wed Aug 23 20:20:59 2017 +0000 @@ -25,86 +25,28 @@ import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; -import java.util.function.BiFunction; - -import org.graalvm.compiler.core.common.type.IntegerStamp; -import org.graalvm.compiler.core.common.type.Stamp; -import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.UMulHigh; import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.calc.BinaryNode; -import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; +import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; -import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.PrimitiveConstant; import jdk.vm.ci.meta.Value; @NodeInfo(shortName = "|*H|", cycles = CYCLES_2, size = SIZE_2) -public final class UnsignedMulHighNode extends BinaryNode implements ArithmeticLIRLowerable { - +public final class UnsignedMulHighNode extends BinaryArithmeticNode implements Canonicalizable.BinaryCommutative { public static final NodeClass TYPE = NodeClass.create(UnsignedMulHighNode.class); public UnsignedMulHighNode(ValueNode x, ValueNode y) { - this((IntegerStamp) x.stamp().unrestricted(), x, y); - } - - public UnsignedMulHighNode(IntegerStamp stamp, ValueNode x, ValueNode y) { - super(TYPE, stamp, x, y); - } - - private static long[] getUnsignedExtremes(IntegerStamp stamp) { - if (stamp.lowerBound() < 0 && stamp.upperBound() >= 0) { - /* - * If -1 and 0 are both in the signed range, then we can't say anything about the - * unsigned range, so we have to return [0, MAX_UNSIGNED]. - */ - return new long[]{0, -1L}; - } else { - return new long[]{stamp.lowerBound(), stamp.upperBound()}; - } - } - - /** - * Determines the minimum and maximum result of this node for the given inputs and returns the - * result of the given BiFunction on the minimum and maximum values. Note that the minima and - * maxima are calculated using signed min/max functions, while the values themselves are - * unsigned. - */ - private T processExtremes(Stamp forX, Stamp forY, BiFunction op) { - IntegerStamp xStamp = (IntegerStamp) forX; - IntegerStamp yStamp = (IntegerStamp) forY; - - JavaKind kind = getStackKind(); - assert kind == JavaKind.Int || kind == JavaKind.Long; - long[] xExtremes = getUnsignedExtremes(xStamp); - long[] yExtremes = getUnsignedExtremes(yStamp); - long min = Long.MAX_VALUE; - long max = Long.MIN_VALUE; - for (long a : xExtremes) { - for (long b : yExtremes) { - long result = kind == JavaKind.Int ? multiplyHighUnsigned((int) a, (int) b) : multiplyHighUnsigned(a, b); - min = Math.min(min, result); - max = Math.max(max, result); - } - } - return op.apply(min, max); - } - - @SuppressWarnings("cast") - @Override - public Stamp foldStamp(Stamp stampX, Stamp stampY) { - // if min is negative, then the value can reach into the unsigned range - return processExtremes(stampX, stampY, (min, max) -> (min == (long) max || min >= 0) ? StampFactory.forInteger(getStackKind(), min, max) : StampFactory.forKind(getStackKind())); - } - - @SuppressWarnings("cast") - @Override - public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - return processExtremes(forX.stamp(), forY.stamp(), (min, max) -> min == (long) max ? ConstantNode.forIntegerKind(getStackKind(), min) : this); + super(TYPE, ArithmeticOpTable::getUMulHigh, x, y); } @Override @@ -114,31 +56,35 @@ nodeValueMap.setResult(this, gen.emitUMulHigh(a, b)); } - public static int multiplyHighUnsigned(int x, int y) { - long xl = x & 0xFFFFFFFFL; - long yl = y & 0xFFFFFFFFL; - long r = xl * yl; - return (int) (r >> 32); + @Override + public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + ValueNode ret = super.canonical(tool, forX, forY); + if (ret != this) { + return ret; + } + + if (forX.isConstant() && !forY.isConstant()) { + // we try to swap and canonicalize + ValueNode improvement = canonical(tool, forY, forX); + if (improvement != this) { + return improvement; + } + // if this fails we only swap + return new UnsignedMulHighNode(forY, forX); + } + return canonical(this, forY); } - public static long multiplyHighUnsigned(long x, long y) { - // Checkstyle: stop - long x0, y0, z0; - long x1, y1, z1, z2, t; - // Checkstyle: resume - - x0 = x & 0xFFFFFFFFL; - x1 = x >>> 32; - - y0 = y & 0xFFFFFFFFL; - y1 = y >>> 32; - - z0 = x0 * y0; - t = x1 * y0 + (z0 >>> 32); - z1 = t & 0xFFFFFFFFL; - z2 = t >>> 32; - z1 += x0 * y1; - - return x1 * y1 + z2 + (z1 >>> 32); + private static ValueNode canonical(UnsignedMulHighNode self, ValueNode forY) { + if (forY.isConstant()) { + Constant c = forY.asConstant(); + if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) { + long i = ((PrimitiveConstant) c).asLong(); + if (i == 0 || i == 1) { + return ConstantNode.forIntegerStamp(self.stamp(), 0); + } + } + } + return self; } } diff -r 9d444ac611a9 -r 612739349279 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.micro.benchmarks/src/micro/benchmarks/TestJMHBlackbox.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.micro.benchmarks/src/micro/benchmarks/TestJMHBlackbox.java Wed Aug 23 20:20:59 2017 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, 2017, 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 micro.benchmarks; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Warmup; + +@Warmup(iterations = 1) +@Measurement(iterations = 1) +@Fork(1) +/** + * This dummy class is used to verify that the JMH microbenchmarking environment is set up properly. + */ +public class TestJMHBlackbox { + + @Benchmark + public void testJMH() { + // This method was intentionally left blank. + } + +}