8185829: Update Graal

Reviewed-by: iveresov
This commit is contained in:
Dean Long 2017-08-04 19:59:33 -07:00
parent d67d35e00a
commit 4c46a93864
78 changed files with 3528 additions and 1132 deletions

View File

@ -179,6 +179,14 @@ suite = {
"workingSets" : "Graal",
},
"org.graalvm.graphio" : {
"subDir" : "share/classes",
"sourceDirs" : ["src"],
"checkstyle" : "org.graalvm.compiler.graph",
"javaCompliance" : "1.8",
"workingSets" : "API,Graal",
},
"org.graalvm.util" : {
"subDir" : "share/classes",
"sourceDirs" : ["src"],
@ -1011,6 +1019,7 @@ suite = {
"subDir" : "share/classes",
"sourceDirs" : ["src"],
"dependencies" : [
"org.graalvm.graphio",
"org.graalvm.compiler.core",
"org.graalvm.compiler.java",
],
@ -1138,6 +1147,13 @@ suite = {
],
},
"GRAAL_GRAPHIO" : {
"subDir" : "share/classes",
"dependencies" : ["org.graalvm.graphio"],
"distDependencies" : [
],
},
"GRAAL_OPTIONS_PROCESSOR" : {
"subDir" : "share/classes",
"dependencies" : ["org.graalvm.compiler.options.processor"],
@ -1203,6 +1219,7 @@ suite = {
"distDependencies" : [
"GRAAL_API",
"GRAAL_COMPILER",
"GRAAL_GRAPHIO",
],
},
@ -1297,6 +1314,7 @@ suite = {
"GRAAL" : {
"subDir" : "share/classes",
"overlaps" : [
"GRAAL_GRAPHIO",
"GRAAL_OPTIONS",
"GRAAL_NODEINFO",
"GRAAL_API",

View File

@ -94,7 +94,7 @@ public class AArch64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implem
}
}
protected Value emitExtendMemory(boolean isSigned, AArch64Kind memoryKind, int resultBits, AArch64AddressValue address, LIRFrameState state) {
public Value emitExtendMemory(boolean isSigned, AArch64Kind memoryKind, int resultBits, AArch64AddressValue 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 ? AArch64Kind.DWORD : AArch64Kind.QWORD));

View File

@ -24,14 +24,9 @@
package org.graalvm.compiler.core.aarch64;
import org.graalvm.compiler.core.gen.NodeMatchRules;
import org.graalvm.compiler.core.match.ComplexMatchResult;
import org.graalvm.compiler.core.match.MatchRule;
import org.graalvm.compiler.lir.LIRFrameState;
import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
import org.graalvm.compiler.nodes.DeoptimizingNode;
import org.graalvm.compiler.nodes.calc.SignExtendNode;
import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
import org.graalvm.compiler.nodes.memory.Access;
import jdk.vm.ci.aarch64.AArch64Kind;
@ -61,18 +56,4 @@ public class AArch64NodeMatchRules extends NodeMatchRules {
protected AArch64ArithmeticLIRGenerator getArithmeticLIRGenerator() {
return (AArch64ArithmeticLIRGenerator) getLIRGeneratorTool().getArithmetic();
}
@MatchRule("(ZeroExtend Read=access)")
@MatchRule("(ZeroExtend FloatingRead=access)")
public ComplexMatchResult zeroExtend(ZeroExtendNode root, Access access) {
AArch64Kind memoryKind = getMemoryKind(access);
return builder -> getArithmeticLIRGenerator().emitExtendMemory(false, memoryKind, root.getResultBits(), (AArch64AddressValue) operand(access.getAddress()), getState(access));
}
@MatchRule("(SignExtend Read=access)")
@MatchRule("(SignExtend FloatingRead=access)")
public ComplexMatchResult signExtend(SignExtendNode root, Access access) {
AArch64Kind memoryKind = getMemoryKind(access);
return builder -> getArithmeticLIRGenerator().emitExtendMemory(true, memoryKind, root.getResultBits(), (AArch64AddressValue) operand(access.getAddress()), getState(access));
}
}

View File

@ -22,7 +22,6 @@
*/
package org.graalvm.compiler.core.common;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionType;
@ -269,9 +268,6 @@ public final class GraalOptions {
@Option(help = "Use a cache for snippet graphs.", type = OptionType.Debug)
public static final OptionKey<Boolean> UseSnippetGraphCache = new OptionKey<>(true);
@Option(help = "Enable expensive assertions.", type = OptionType.Debug)
public static final OptionKey<Boolean> DetailedAsserts = new OptionKey<>(Assertions.ENABLED);
@Option(help = "Enable experimental Trace Register Allocation.", type = OptionType.Debug)
public static final OptionKey<Boolean> TraceRA = new OptionKey<>(false);

View File

@ -111,6 +111,7 @@ import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase;
import org.graalvm.compiler.phases.schedule.SchedulePhase;
import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
import org.graalvm.compiler.phases.tiers.HighTierContext;
import org.graalvm.compiler.phases.tiers.MidTierContext;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.phases.tiers.TargetProvider;
import org.graalvm.compiler.phases.util.Providers;
@ -595,6 +596,10 @@ public abstract class GraalCompilerTest extends GraalTest {
return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
}
protected MidTierContext getDefaultMidTierContext() {
return new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null);
}
protected SnippetReflectionProvider getSnippetReflection() {
return Graal.getRequiredCapability(SnippetReflectionProvider.class);
}
@ -926,7 +931,7 @@ public abstract class GraalCompilerTest extends GraalTest {
*/
@SuppressWarnings("try")
protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) {
if (!forceCompile) {
if (!forceCompile && graph == null) {
InstalledCode cached = cache.get(installedCodeOwner);
if (cached != null) {
if (cached.isValid()) {

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.core.test;
import org.junit.Test;
/*
* Test compilation of ZeroExtend and SignExtend nodes
*/
public class ZeroSignExtendTest extends GraalCompilerTest {
int testSnippet1(char[] chars) {
int x = 1;
x += chars[0];
x -= chars[1];
x *= chars[2];
x /= chars[3];
x &= chars[4];
x |= chars[5];
x ^= chars[6];
x <<= chars[7];
x >>= (chars[8] - chars[0]);
x >>>= (chars[9] - chars[0]);
x += chars[1];
return x;
}
long testSnippet2(char[] chars) {
long y = 2;
y += chars[0];
y -= chars[1];
y *= chars[2];
y /= chars[3];
y &= chars[4];
y |= chars[5];
y ^= chars[6];
y <<= chars[7];
y >>= (chars[8] - chars[0]);
y >>>= (chars[9] - chars[0]);
y += chars[1];
return y;
}
int testSnippet3(short[] shorts) {
int x = 1;
x += shorts[0];
x -= shorts[1];
x *= shorts[2];
x /= shorts[3];
x &= shorts[4];
x |= shorts[5];
x ^= shorts[6];
x <<= shorts[7];
x >>= (shorts[8] - shorts[0]);
x >>>= (shorts[9] - shorts[0]);
x += shorts[1];
return x;
}
long testSnippet4(short[] shorts) {
long y = 2;
y += shorts[0];
y -= shorts[1];
y *= shorts[2];
y /= shorts[3];
y &= shorts[4];
y |= shorts[5];
y ^= shorts[6];
y <<= shorts[7];
y >>= (shorts[8] - shorts[0]);
y >>>= (shorts[9] - shorts[0]);
y += shorts[1];
return y;
}
int testSnippet5(byte[] bytes) {
int x = 1;
x += bytes[0];
x -= bytes[1];
x *= bytes[2];
x /= bytes[3];
x &= bytes[4];
x |= bytes[5];
x ^= bytes[6];
x <<= bytes[7];
x >>= (bytes[8] - bytes[0]);
x >>>= (bytes[9] - bytes[0]);
x += bytes[1];
return x;
}
long testSnippet6(byte[] bytes) {
long y = 2;
y += bytes[0];
y -= bytes[1];
y *= bytes[2];
y /= bytes[3];
y &= bytes[4];
y |= bytes[5];
y ^= bytes[6];
y <<= bytes[7];
y >>= (bytes[8] - bytes[0]);
y >>>= (bytes[9] - bytes[0]);
y += bytes[1];
return y;
}
@Test
public void test() {
char[] input1 = new char[]{'0', '1', '2', '3', '4', '5', '7', '8', '9', 'A'};
char[] input2 = new char[]{'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'};
short[] input3 = new short[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
short[] input4 = new short[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
byte[] input5 = new byte[]{21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
byte[] input6 = new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 40};
test("testSnippet1", input1);
test("testSnippet2", input2);
test("testSnippet3", input3);
test("testSnippet4", input4);
test("testSnippet5", input5);
test("testSnippet6", input6);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.test.ea;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.junit.Test;
public class CountUppercaseParallelTest extends GraalCompilerTest {
public static long count(CharSequence sentence) {
return sentence.chars().parallel().filter(c -> Character.isUpperCase(c)).count();
}
@Test
public void testCount() {
String sequence = "In 2017 I would like to run ALL languages in one VM.";
for (int i = 0; i < 5000; i++) {
count(sequence);
}
test("count", sequence);
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.test.ea;
import java.util.HashSet;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.nodes.debug.BlackholeNode;
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
import org.junit.Assert;
import org.junit.Test;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.meta.ResolvedJavaMethod;
/*
* Test whether complex tree structures properly maintain identity.
*/
public class PartialEscapeAnalysisTreesTest extends EATestBase {
static class TreeNode {
TreeNode left;
TreeNode right;
TreeNode() {
}
TreeNode(TreeNode left, TreeNode right) {
this.left = left;
this.right = right;
}
public void visit(HashSet<TreeNode> instances) {
instances.add(this);
if (left != null) {
left.visit(instances);
}
if (right != null) {
right.visit(instances);
}
}
int countInstances() {
HashSet<TreeNode> instances = new HashSet<>();
visit(instances);
return instances.size();
}
}
public static TreeNode buildTree(boolean a) {
TreeNode leftChild;
TreeNode rightChild;
TreeNode taskToFork;
TreeNode task;
if (a) {
GraalDirectives.blackhole(new TreeNode());
leftChild = new TreeNode();
rightChild = new TreeNode();
task = new TreeNode(leftChild, rightChild);
taskToFork = rightChild;
GraalDirectives.blackhole(task);
} else {
leftChild = new TreeNode();
rightChild = new TreeNode();
task = new TreeNode(leftChild, rightChild);
taskToFork = leftChild;
GraalDirectives.blackhole(task);
}
if (taskToFork.left == null) {
taskToFork.left = new TreeNode();
}
return new TreeNode(task, null);
}
@Test
public void testBuildTree() {
testGraph("buildTree");
}
/**
* Prepare a graph that includes some blackholes and then remove the blackholes and compile
* normally to create an unusual situation for PEA.
*/
@SuppressWarnings("try")
public void testGraph(String name) {
ResolvedJavaMethod method = getResolvedJavaMethod(name);
prepareGraph(name, true);
try (DebugContext.Scope s = graph.getDebug().scope(getClass(), method, getCodeCache(), graph)) {
for (BlackholeNode node : graph.getNodes().filter(BlackholeNode.class)) {
graph.removeFixed(node);
}
new DeadCodeEliminationPhase().apply(graph);
new CanonicalizerPhase().apply(graph, context);
InstalledCode code = getCode(method, graph, true);
GraalCompilerTest.Result r = executeExpected(method, null, true);
int expectedInstances = ((TreeNode) r.returnValue).countInstances();
TreeNode r2 = (TreeNode) code.executeVarargs(true);
Assert.assertEquals("Wrong number of nodes in tree", expectedInstances, r2.countInstances());
r = executeExpected(method, null, false);
expectedInstances = ((TreeNode) r.returnValue).countInstances();
r2 = (TreeNode) code.executeVarargs(false);
Assert.assertEquals("Wrong number of nodes in tree", expectedInstances, r2.countInstances());
} catch (Throwable e) {
throw graph.getDebug().handle(e);
}
}
}

View File

@ -38,6 +38,10 @@ public class UnsafeEATest extends EATestBase {
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"));
@ -51,6 +55,9 @@ public class UnsafeEATest extends EATestBase {
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);
}
@ -195,4 +202,77 @@ public class UnsafeEATest extends EATestBase {
}
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");
}
}

View File

@ -227,7 +227,7 @@ public class DebugContextTest {
*/
@Test
public void testInvariantChecking() throws InterruptedException {
Assume.assumeTrue(Assertions.ENABLED);
Assume.assumeTrue(Assertions.assertionsEnabled());
EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
// Configure with an option that enables counters
map.put(DebugOptions.Counters, "");

View File

@ -22,6 +22,11 @@
*/
package org.graalvm.compiler.debug;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionType;
import org.graalvm.compiler.options.OptionValues;
/**
* Utility for query whether assertions are enabled.
*/
@ -30,11 +35,28 @@ public class Assertions {
* Determines if assertions are enabled. Strictly speaking, this may only be true for the
* {@link Assertions} class but we assume assertions are enabled/disabled for Graal as a whole.
*/
public static final boolean ENABLED = assertionsEnabled();
private static boolean assertionsEnabled() {
public static boolean assertionsEnabled() {
boolean enabled = false;
assert (enabled = true) == true;
return enabled;
}
/**
* Determines if detailed assertions are enabled. This requires that the normal assertions are
* also enabled.
*
* @param values the current OptionValues that might define a value for DetailAsserts.
*/
public static boolean detailedAssertionsEnabled(OptionValues values) {
return assertionsEnabled() && Options.DetailedAsserts.getValue(values);
}
// @formatter:off
public static class Options {
@Option(help = "Enable expensive assertions. (Require normal assertions enabled)", type = OptionType.Debug)
public static final OptionKey<Boolean> DetailedAsserts = new OptionKey<>(true);
}
// @formatter:on
}

View File

@ -65,7 +65,7 @@ final class DebugConfigImpl implements DebugConfig {
DebugOptions.TrackMemUse.getValue(options),
DebugOptions.Time.getValue(options),
DebugOptions.Dump.getValue(options),
DebugOptions.Verify.getValue(options),
getVerifyOptionValue(options),
DebugOptions.MethodFilter.getValue(options),
output, dumpHandlers, verifyHandlers);
}
@ -99,6 +99,10 @@ final class DebugConfigImpl implements DebugConfig {
this.output = output;
}
private static String getVerifyOptionValue(OptionValues values) {
return !DebugOptions.Verify.hasBeenSet(values) && Assertions.assertionsEnabled() ? "" : DebugOptions.Verify.getValue(values);
}
@Override
public OptionValues getOptions() {
return options;

View File

@ -32,11 +32,9 @@ import static org.graalvm.compiler.debug.DebugOptions.DumpOnPhaseChange;
import static org.graalvm.compiler.debug.DebugOptions.ListMetrics;
import static org.graalvm.compiler.debug.DebugOptions.Log;
import static org.graalvm.compiler.debug.DebugOptions.MemUseTrackers;
import static org.graalvm.compiler.debug.DebugOptions.MethodFilter;
import static org.graalvm.compiler.debug.DebugOptions.Time;
import static org.graalvm.compiler.debug.DebugOptions.Timers;
import static org.graalvm.compiler.debug.DebugOptions.TrackMemUse;
import static org.graalvm.compiler.debug.DebugOptions.Verify;
import java.io.ByteArrayOutputStream;
import java.io.File;
@ -374,16 +372,7 @@ public final class DebugContext implements AutoCloseable {
}
}
}
currentConfig = new DebugConfigImpl(
options,
Log.getValue(options),
Count.getValue(options),
TrackMemUse.getValue(options),
Time.getValue(options),
Dump.getValue(options),
Verify.getValue(options),
MethodFilter.getValue(options),
logStream, dumpHandlers, verifyHandlers);
currentConfig = new DebugConfigImpl(options, logStream, dumpHandlers, verifyHandlers);
currentScope = new ScopeImpl(this, Thread.currentThread());
currentScope.updateFlags(currentConfig);
metricsEnabled = true;
@ -575,7 +564,7 @@ public final class DebugContext implements AutoCloseable {
}
}
private final Invariants invariants = Assertions.ENABLED ? new Invariants() : null;
private final Invariants invariants = Assertions.assertionsEnabled() ? new Invariants() : null;
static StackTraceElement[] getStackTrace(Thread thread) {
return thread.getStackTrace();

View File

@ -75,7 +75,7 @@ public class DebugOptions {
public static final OptionKey<String> Time = new OptionKey<>(null);
@Option(help = "Pattern for scope(s) in which verification is enabled (see DebugFilter and Debug.verify).", type = OptionType.Debug)
public static final OptionKey<String> Verify = new OptionKey<>(Assertions.ENABLED ? "" : null);
public static final OptionKey<String> Verify = new OptionKey<>(null);
@Option(help = "Pattern for scope(s) in which dumping is enabled (see DebugFilter and Debug.dump)", type = OptionType.Debug)
public static final OptionKey<String> Dump = new OptionKey<>(null);
@Option(help = "Pattern for scope(s) in which logging is enabled (see DebugFilter and Debug.log)", type = OptionType.Debug)
@ -115,7 +115,7 @@ public class DebugOptions {
@Option(help = "The directory where various Graal dump files are written.")
public static final OptionKey<String> DumpPath = new OptionKey<>("dumps");
@Option(help = "Print the name of each dump file path as it's created.")
public static final OptionKey<Boolean> ShowDumpFiles = new OptionKey<>(Assertions.ENABLED);
public static final OptionKey<Boolean> ShowDumpFiles = new OptionKey<>(false);
@Option(help = "Enable dumping to the C1Visualizer. Enabling this option implies PrintBackendCFG.", type = OptionType.Debug)
public static final OptionKey<Boolean> PrintCFG = new OptionKey<>(false);

View File

@ -148,7 +148,8 @@ public class DiagnosticsOutputDirectory {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
ZipEntry ze = new ZipEntry(file.toString());
String name = dir.relativize(file).toString();
ZipEntry ze = new ZipEntry(name);
zos.putNextEntry(ze);
zos.write(Files.readAllBytes(file));
zos.closeEntry();

View File

@ -119,7 +119,7 @@ public class NodeMapTest extends GraphTest {
TestNode newNode = graph.add(new TestNode());
try {
map.get(newNode);
fail("expected " + (Assertions.ENABLED ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
fail("expected " + (Assertions.assertionsEnabled() ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
} catch (AssertionError ae) {
// thrown when assertions are enabled
} catch (ArrayIndexOutOfBoundsException e) {
@ -136,7 +136,7 @@ public class NodeMapTest extends GraphTest {
TestNode newNode = graph.add(new TestNode());
try {
map.set(newNode, 1);
fail("expected " + (Assertions.ENABLED ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
fail("expected " + (Assertions.assertionsEnabled() ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
} catch (AssertionError ae) {
// thrown when assertions are enabled
} catch (ArrayIndexOutOfBoundsException e) {

View File

@ -31,9 +31,11 @@ import org.graalvm.compiler.phases.BasePhase;
import org.graalvm.compiler.phases.common.AddressLoweringByUsePhase;
import org.graalvm.compiler.phases.common.ExpandLogicPhase;
import org.graalvm.compiler.phases.common.FixReadsPhase;
import org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase;
import org.graalvm.compiler.phases.tiers.LowTierContext;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.phases.tiers.SuitesCreator;
import org.graalvm.compiler.replacements.aarch64.AArch64ReadReplacementPhase;
import java.util.ListIterator;
@ -60,6 +62,9 @@ public class AArch64HotSpotSuitesProvider extends HotSpotSuitesProvider {
}
findPhase.add(new AddressLoweringByUsePhase(addressLoweringByUse));
findPhase = suites.getLowTier().findPhase(PropagateDeoptimizeProbabilityPhase.class);
findPhase.add(new AArch64ReadReplacementPhase());
return suites;
}
}

View File

@ -23,7 +23,7 @@
package org.graalvm.compiler.hotspot.amd64;
import org.graalvm.compiler.core.amd64.AMD64SuitesCreator;
import org.graalvm.compiler.core.common.GraalOptions;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.hotspot.lir.HotSpotZapRegistersPhase;
import org.graalvm.compiler.lir.phases.LIRSuites;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
@ -39,7 +39,7 @@ public class AMD64HotSpotSuitesCreator extends AMD64SuitesCreator {
@Override
public LIRSuites createLIRSuites(OptionValues options) {
LIRSuites lirSuites = super.createLIRSuites(options);
if (GraalOptions.DetailedAsserts.getValue(options)) {
if (Assertions.detailedAssertionsEnabled(options)) {
lirSuites.getPostAllocationOptimizationStage().appendPhase(new HotSpotZapRegistersPhase());
}
return lirSuites;

View File

@ -82,6 +82,8 @@ public class CompilationWrapperTest extends GraalCompilerTest {
"test");
}
private static final boolean VERBOSE = Boolean.getBoolean(CompilationWrapperTest.class.getSimpleName() + ".verbose");
private static void testHelper(List<String> extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException {
final File dumpPath = new File(CompilationWrapperTest.class.getSimpleName() + "_" + System.currentTimeMillis()).getAbsoluteFile();
List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
@ -92,7 +94,9 @@ public class CompilationWrapperTest extends GraalCompilerTest {
vmArgs.addAll(extraVmArgs);
Subprocess proc = SubprocessUtil.java(vmArgs, mainClassAndArgs);
System.out.println(proc);
if (VERBOSE) {
System.out.println(proc);
}
String forcedCrashString = "Forced crash after compiling";
String diagnosticOutputFilePrefix = "Graal diagnostic output saved in ";

View File

@ -53,6 +53,7 @@ import jdk.vm.ci.hotspot.EventProvider;
import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
import jdk.vm.ci.hotspot.HotSpotInstalledCode;
import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
@ -145,7 +146,7 @@ public class CompilationTask {
* respect CompilationFailureAction if it has been explicitly set.
*/
if (actionKey == CompilationFailureAction && !actionKey.hasBeenSet(values)) {
if (Assertions.ENABLED || compiler.getGraalRuntime().isBootstrapping()) {
if (Assertions.assertionsEnabled() || compiler.getGraalRuntime().isBootstrapping()) {
return ExitVM;
}
}
@ -305,6 +306,10 @@ public class CompilationTask {
if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
return HotSpotCompilationRequestResult.failure("Already compiled", false);
}
if (HotSpotGraalCompilerFactory.checkGraalCompileOnlyFilter(method.getDeclaringClass().toJavaName(), method.getName(), method.getSignature().toString(),
HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) != HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) {
return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false);
}
}
HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper(compilationEvent);

View File

@ -130,7 +130,7 @@ public abstract class CompilerConfigurationFactory implements Comparable<Compile
* List used to assert uniqueness of {@link #name} and {@link #autoSelectionPriority} across all
* {@link CompilerConfigurationFactory} instances.
*/
private static final List<CompilerConfigurationFactory> factories = Assertions.ENABLED ? new ArrayList<>() : null;
private static final List<CompilerConfigurationFactory> factories = Assertions.assertionsEnabled() ? new ArrayList<>() : null;
private static boolean checkAndAddNewFactory(CompilerConfigurationFactory factory) {
for (CompilerConfigurationFactory other : factories) {

View File

@ -162,9 +162,20 @@ public final class HotSpotGraalCompilerFactory extends HotSpotJVMCICompilerFacto
* This method is static so it can be exercised during initialization.
*/
private static CompilationLevel adjustCompilationLevelInternal(Class<?> declaringClass, String name, String signature, CompilationLevel level) {
if (compileGraalWithC1Only) {
if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
String declaringClassName = declaringClass.getName();
if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm") || declaringClassName.startsWith("com.oracle.graal")) {
return CompilationLevel.Simple;
}
}
}
return checkGraalCompileOnlyFilter(declaringClass.getName(), name, signature, level);
}
public static CompilationLevel checkGraalCompileOnlyFilter(String declaringClassName, String name, String signature, CompilationLevel level) {
if (graalCompileOnlyFilter != null) {
if (level == CompilationLevel.FullOptimization) {
String declaringClassName = declaringClass.getName();
HotSpotSignature sig = null;
for (MethodFilter filter : graalCompileOnlyFilter) {
if (filter.hasSignature() && sig == null) {
@ -177,14 +188,6 @@ public final class HotSpotGraalCompilerFactory extends HotSpotJVMCICompilerFacto
return CompilationLevel.Simple;
}
}
if (compileGraalWithC1Only) {
if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
String declaringClassName = declaringClass.getName();
if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm") || declaringClassName.startsWith("com.oracle.graal")) {
return CompilationLevel.Simple;
}
}
}
return level;
}
}

View File

@ -138,8 +138,6 @@ class JVMCIVersionCheck {
if (build >= JVMCI9_MIN_EA_BUILD) {
return;
}
// Using Object.equals suppresses Eclipse's "Dead code" warning.
// Unfortunately @SuppressWarnings("unused") can only be applied at method level.
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 {

View File

@ -153,7 +153,7 @@ public class ExceptionHandlerStub extends SnippetStub {
@Fold
@SuppressWarnings("all")
static boolean assertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) {
return Assertions.ENABLED || cAssertionsEnabled(config);
return Assertions.assertionsEnabled() || cAssertionsEnabled(config);
}
public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_PC = newDescriptor(ExceptionHandlerStub.class, "exceptionHandlerForPc", Word.class, Word.class);

View File

@ -116,7 +116,7 @@ public class UnwindExceptionToCallerStub extends SnippetStub {
@Fold
@SuppressWarnings("all")
static boolean assertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) {
return Assertions.ENABLED || cAssertionsEnabled(config);
return Assertions.assertionsEnabled() || cAssertionsEnabled(config);
}
public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_RETURN_ADDRESS = newDescriptor(UnwindExceptionToCallerStub.class, "exceptionHandlerForReturnAddress", Word.class, Word.class,

View File

@ -371,6 +371,7 @@ import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
import org.graalvm.compiler.nodes.extended.LoadHubNode;
import org.graalvm.compiler.nodes.extended.LoadMethodNode;
import org.graalvm.compiler.nodes.extended.MembarNode;
import org.graalvm.compiler.nodes.extended.StateSplitProxyNode;
import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
import org.graalvm.compiler.nodes.graphbuilderconf.ClassInitializationPlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
@ -1236,6 +1237,10 @@ public class BytecodeParser implements GraphBuilderContext {
}
}
protected StateSplitProxyNode genVolatileFieldReadProxy(ValueNode fieldRead) {
return new StateSplitProxyNode(fieldRead);
}
protected ValueNode emitExplicitNullCheck(ValueNode receiver) {
if (StampTool.isPointerNonNull(receiver.stamp())) {
return receiver;
@ -1429,7 +1434,7 @@ public class BytecodeParser implements GraphBuilderContext {
createNonInlinedInvoke(withExceptionEdge, bci(), callTarget, resultType);
}
private Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
protected Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
ResolvedJavaMethod targetMethod = initialTargetMethod;
InvokeKind invokeKind = initialInvokeKind;
if (initialInvokeKind.isIndirect()) {
@ -1676,7 +1681,7 @@ public class BytecodeParser implements GraphBuilderContext {
final Mark mark;
InvocationPluginAssertions(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) {
guarantee(Assertions.ENABLED, "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
guarantee(Assertions.assertionsEnabled(), "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
this.plugin = plugin;
this.targetMethod = targetMethod;
this.args = args;
@ -1908,7 +1913,7 @@ public class BytecodeParser implements GraphBuilderContext {
}
}
InvocationPluginAssertions assertions = Assertions.ENABLED ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
InvocationPluginAssertions assertions = Assertions.assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
if (plugin.execute(this, targetMethod, pluginReceiver, args)) {
afterInvocationPluginExecution(true, assertions, intrinsicGuard, invokeKind, args, targetMethod, resultType, returnType);
return true;
@ -3788,11 +3793,22 @@ public class BytecodeParser implements GraphBuilderContext {
}
}
frameState.push(resolvedField.getJavaKind(), append(genLoadField(receiver, resolvedField)));
ValueNode fieldRead = append(genLoadField(receiver, resolvedField));
if (resolvedField.getDeclaringClass().getName().equals("Ljava/lang/ref/Reference;") && resolvedField.getName().equals("referent")) {
LocationIdentity referentIdentity = new FieldLocationIdentity(resolvedField);
append(new MembarNode(0, referentIdentity));
}
JavaKind fieldKind = resolvedField.getJavaKind();
if (resolvedField.isVolatile() && fieldRead instanceof LoadFieldNode) {
StateSplitProxyNode readProxy = append(genVolatileFieldReadProxy(fieldRead));
frameState.push(fieldKind, readProxy);
readProxy.setStateAfter(frameState.create(stream.nextBCI(), readProxy));
} else {
frameState.push(fieldKind, fieldRead);
}
}
/**

View File

@ -22,7 +22,6 @@
*/
package org.graalvm.compiler.lir.alloc.lsra;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
@ -39,6 +38,7 @@ import java.util.List;
import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.core.common.util.IntList;
import org.graalvm.compiler.core.common.util.Util;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.Variable;
@ -1140,7 +1140,7 @@ public final class Interval {
// split list of use positions
result.usePosList = usePosList.splitAt(splitPos);
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
for (int i = 0; i < usePosList.size(); i++) {
assert usePosList.usePos(i) < splitPos;
}

View File

@ -27,7 +27,6 @@ import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.isIllegal;
import static jdk.vm.ci.code.ValueUtil.isLegal;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
@ -40,6 +39,7 @@ import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.cfg.BlockMap;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.Indent;
@ -206,7 +206,7 @@ public class LinearScan {
this.rangeEndMarker = new Range(Integer.MAX_VALUE, Integer.MAX_VALUE, null);
this.intervalEndMarker = new Interval(Value.ILLEGAL, Interval.END_MARKER_OPERAND_NUMBER, null, rangeEndMarker);
this.intervalEndMarker.next = intervalEndMarker;
this.detailedAsserts = DetailedAsserts.getValue(ir.getOptions());
this.detailedAsserts = Assertions.detailedAssertionsEnabled(ir.getOptions());
}
public LIRGenerationResult getLIRGenerationResult() {

View File

@ -23,7 +23,6 @@
package org.graalvm.compiler.lir.alloc.lsra;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
@ -31,6 +30,7 @@ import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
import java.util.ArrayList;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.lir.LIRInsertionBuffer;
@ -99,7 +99,7 @@ public class LinearScanEliminateSpillMovePhase extends LinearScanAllocationPhase
*/
Interval interval;
interval = allocator.createUnhandledLists(mustStoreAtDefinition, null).getLeft();
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
checkIntervals(debug, interval);
}

View File

@ -26,7 +26,6 @@ import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.asStackSlot;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static jdk.vm.ci.code.ValueUtil.isStackSlot;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import static org.graalvm.compiler.lir.debug.LIRGenerationDebugContext.getSourceForOperandFromDebugContext;
@ -41,6 +40,7 @@ import org.graalvm.compiler.core.common.PermanentBailoutException;
import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.util.BitMap2D;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.Indent;
@ -88,7 +88,7 @@ public class LinearScanLifetimeAnalysisPhase extends LinearScanAllocationPhase {
debug.dump(DebugContext.VERBOSE_LEVEL, lirGenRes.getLIR(), "Before register allocation");
computeLocalLiveSets();
computeGlobalLiveSets();
buildIntervals(DetailedAsserts.getValue(allocator.getOptions()));
buildIntervals(Assertions.detailedAssertionsEnabled(allocator.getOptions()));
}
/**
@ -364,14 +364,14 @@ public class LinearScanLifetimeAnalysisPhase extends LinearScanAllocationPhase {
}
} while (changeOccurred);
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
verifyLiveness();
}
// check that the liveIn set of the first block is empty
AbstractBlockBase<?> startBlock = allocator.getLIR().getControlFlowGraph().getStartBlock();
if (allocator.getBlockData(startBlock).liveIn.cardinality() != 0) {
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
reportFailure(numBlocks);
}
// bailout if this occurs in product mode.
@ -570,7 +570,7 @@ public class LinearScanLifetimeAnalysisPhase extends LinearScanAllocationPhase {
ValueMoveOp move = ValueMoveOp.asValueMoveOp(op);
if (optimizeMethodArgument(move.getInput())) {
StackSlot slot = asStackSlot(move.getInput());
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
assert op.id() > 0 : "invalid id";
assert allocator.blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block";
assert isVariable(move.getResult()) : "result of move must be a variable";

View File

@ -26,7 +26,6 @@ import static jdk.vm.ci.code.ValueUtil.asAllocatableValue;
import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.isIllegal;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
@ -42,6 +41,7 @@ import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig.Allocatab
import org.graalvm.compiler.core.common.alloc.Trace;
import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.lir.InstructionValueProcedure;
@ -224,7 +224,7 @@ public final class BottomUpAllocator extends TraceAllocationPhase<TraceAllocatio
debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
}
if (DetailedAsserts.getValue(getLIR().getOptions())) {
if (Assertions.detailedAssertionsEnabled(getLIR().getOptions())) {
assert lir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
/*

View File

@ -26,7 +26,6 @@ import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.isIllegal;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static jdk.vm.ci.code.ValueUtil.isStackSlot;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
@ -39,6 +38,7 @@ import java.util.List;
import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.core.common.util.Util;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.Variable;
@ -722,7 +722,7 @@ final class TraceInterval extends IntervalHint {
// do not add use positions for precolored intervals because they are never used
if (registerPriority != RegisterPriority.None) {
if (DetailedAsserts.getValue(options)) {
if (Assertions.detailedAssertionsEnabled(options)) {
for (int i = 0; i < numUsePos(); i++) {
assert pos <= getUsePos(i) : "already added a use-position with lower position";
if (i > 0) {
@ -802,7 +802,7 @@ final class TraceInterval extends IntervalHint {
// split list of use positions
splitUsePosAt(result, splitPos);
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
for (int i = 0; i < numUsePos(); i++) {
assert getUsePos(i) < splitPos;
}

View File

@ -23,7 +23,6 @@
package org.graalvm.compiler.lir.alloc.trace.lsra;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
@ -34,6 +33,7 @@ import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
import org.graalvm.compiler.core.common.alloc.Trace;
import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
import org.graalvm.compiler.lir.LIRInsertionBuffer;
@ -84,7 +84,7 @@ final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAlloca
* by Interval.spillDefinitionPos.
*/
TraceInterval interval = allocator.createUnhandledListBySpillPos(spilledIntervals);
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
checkIntervals(debug, interval);
}
if (debug.isLogEnabled()) {

View File

@ -27,7 +27,6 @@ import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.isIllegal;
import static jdk.vm.ci.code.ValueUtil.isLegal;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
import java.util.ArrayList;
@ -39,6 +38,7 @@ import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
import org.graalvm.compiler.core.common.alloc.Trace;
import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.Indent;
@ -603,7 +603,7 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE.apply(target, lirGenRes, trace, spillMoveFactory, registerAllocationConfig, traceBuilderResult, this, false);
if (DetailedAsserts.getValue(options)) {
if (Assertions.detailedAssertionsEnabled(options)) {
verifyIntervals();
}
} catch (Throwable e) {

View File

@ -23,7 +23,6 @@
package org.graalvm.compiler.lir.alloc.trace.lsra;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.asConstant;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
@ -36,6 +35,7 @@ import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
import org.graalvm.compiler.core.common.alloc.Trace;
import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.CounterKey;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.Indent;
@ -97,7 +97,7 @@ final class TraceLinearScanResolveDataFlowPhase extends TraceLinearScanAllocatio
debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
}
if (DetailedAsserts.getValue(allocator.getOptions())) {
if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
assert allocator.getLIR().getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
/*

View File

@ -171,7 +171,7 @@ public class CompilationResultBuilder {
assert frameContext != null;
this.dataCache = dataCache;
if (dataBuilder.needDetailedPatchingInformation() || Assertions.ENABLED) {
if (dataBuilder.needDetailedPatchingInformation() || Assertions.assertionsEnabled()) {
/*
* Always enabled in debug mode, even when the VM does not request detailed information,
* to increase test coverage.

View File

@ -24,7 +24,7 @@ package org.graalvm.compiler.lir.phases;
import static org.graalvm.compiler.core.common.GraalOptions.TraceRA;
import org.graalvm.compiler.core.common.GraalOptions;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.lir.alloc.AllocationStageVerifier;
import org.graalvm.compiler.lir.alloc.lsra.LinearScanPhase;
import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessAnalysisPhase;
@ -58,7 +58,7 @@ public class AllocationStage extends LIRPhaseSuite<AllocationContext> {
// currently we mark locations only if we do register allocation
appendPhase(new LocationMarkerPhase());
if (GraalOptions.DetailedAsserts.getValue(options)) {
if (Assertions.detailedAssertionsEnabled(options)) {
appendPhase(new AllocationStageVerifier());
}
}

View File

@ -45,12 +45,12 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
protected void run(StructuredGraph graph, PhaseContext context) {
if (graph.hasLoops()) {
HashSetNodeEventListener listener = new HashSetNodeEventListener();
try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
boolean changed = true;
while (changed) {
boolean changed = true;
while (changed) {
changed = false;
try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
LoopsData dataCounted = new LoopsData(graph);
dataCounted.detectedCountedLoops();
changed = false;
for (LoopEx loop : dataCounted.countedLoops()) {
if (!LoopTransformations.isUnrollableLoop(loop)) {
continue;
@ -60,19 +60,20 @@ public class LoopPartialUnrollPhase extends LoopPhase<LoopPolicies> {
// First perform the pre/post transformation and do the partial
// unroll when we come around again.
LoopTransformations.insertPrePostLoops(loop, graph);
changed = true;
} else {
changed |= LoopTransformations.partialUnroll(loop, graph);
LoopTransformations.partialUnroll(loop, graph);
}
changed = true;
}
}
dataCounted.deleteUnusedNodes();
if (!listener.getNodes().isEmpty()) {
canonicalizer.applyIncremental(graph, context, listener.getNodes());
listener.getNodes().clear();
}
}
}
if (!listener.getNodes().isEmpty()) {
canonicalizer.applyIncremental(graph, context, listener.getNodes());
listener.getNodes().clear();
}
}
}

View File

@ -22,25 +22,13 @@
*/
package org.graalvm.compiler.loop.phases;
import static org.graalvm.compiler.core.common.GraalOptions.MaximumDesiredSize;
import static org.graalvm.compiler.loop.MathUtil.add;
import static org.graalvm.compiler.loop.MathUtil.sub;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.graalvm.compiler.core.common.RetryableBailoutException;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Graph.Mark;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeWorkList;
import org.graalvm.compiler.graph.Position;
import org.graalvm.compiler.loop.BasicInductionVariable;
import org.graalvm.compiler.loop.CountedLoopInfo;
import org.graalvm.compiler.loop.DerivedInductionVariable;
import org.graalvm.compiler.loop.InductionVariable;
import org.graalvm.compiler.loop.InductionVariable.Direction;
import org.graalvm.compiler.loop.LoopEx;
@ -59,23 +47,27 @@ import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.LoopEndNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.SafepointNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.ValuePhiNode;
import org.graalvm.compiler.nodes.calc.AddNode;
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
import org.graalvm.compiler.nodes.calc.CompareNode;
import org.graalvm.compiler.nodes.calc.ConditionalNode;
import org.graalvm.compiler.nodes.calc.IntegerLessThanNode;
import org.graalvm.compiler.nodes.calc.SubNode;
import org.graalvm.compiler.nodes.extended.SwitchNode;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.tiers.PhaseContext;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.graalvm.compiler.core.common.GraalOptions.MaximumDesiredSize;
import static org.graalvm.compiler.loop.MathUtil.add;
import static org.graalvm.compiler.loop.MathUtil.sub;
public abstract class LoopTransformations {
private LoopTransformations() {
@ -154,254 +146,13 @@ public abstract class LoopTransformations {
// TODO (gd) probabilities need some amount of fixup.. (probably also in other transforms)
}
public static boolean partialUnroll(LoopEx loop, StructuredGraph graph) {
public static void partialUnroll(LoopEx loop, StructuredGraph graph) {
assert loop.loopBegin().isMainLoop();
graph.getDebug().log("LoopPartialUnroll %s", loop);
boolean changed = false;
CountedLoopInfo mainCounted = loop.counted();
LoopBeginNode mainLoopBegin = loop.loopBegin();
InductionVariable iv = mainCounted.getCounter();
IfNode mainLimit = mainCounted.getLimitTest();
LogicNode ifTest = mainLimit.condition();
CompareNode compareNode = (CompareNode) ifTest;
ValueNode compareBound = null;
ValueNode curPhi = iv.valueNode();
if (compareNode.getX() == curPhi) {
compareBound = compareNode.getY();
} else if (compareNode.getY() == curPhi) {
compareBound = compareNode.getX();
}
LoopFragmentInside newSegment = loop.inside().duplicate();
newSegment.insertWithinAfter(loop);
graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication inside %s", mainLoopBegin);
ValueNode inductionNode = iv.valueNode();
Node newStrideNode = null;
for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
Node segmentOrigOp = null;
Node replacementOp = null;
changed = false;
// Rework each phi with a loop carried dependence
for (Node phiUsage : mainPhiNode.usages()) {
if (!loop.isOutsideLoop(phiUsage)) {
for (int i = 1; i < mainPhiNode.valueCount(); i++) {
ValueNode v = mainPhiNode.valueAt(i);
if (mainPhiNode != inductionNode) {
if (closureOnPhiInputToPhiUse(v, phiUsage, loop, graph)) {
segmentOrigOp = v;
Node node = newSegment.getDuplicatedNode(v);
replacementOp = updateUnrollSegmentValue(mainPhiNode, inductionNode, phiUsage, v, newSegment);
// Update the induction phi with new stride node
mainPhiNode.setValueAt(i, (ValueNode) node);
// This is for induction variables not referenced in the loop body
if (inductionNode == v) {
newStrideNode = node;
}
changed = true;
break;
}
} else if (v == phiUsage) {
segmentOrigOp = phiUsage;
Node node = newSegment.getDuplicatedNode(phiUsage);
newStrideNode = node;
replacementOp = updateUnrollSegmentValue(mainPhiNode, inductionNode, phiUsage, phiUsage, newSegment);
// Update the induction phi with new stride node
mainPhiNode.setValueAt(i, (ValueNode) node);
changed = true;
break;
}
}
}
if (changed) {
break;
}
}
if (changed) {
// Patch the new segments induction uses of replacementOp with the old stride node
for (Node usage : mainPhiNode.usages()) {
if (usage != segmentOrigOp) {
if (!loop.isOutsideLoop(usage)) {
Node node = newSegment.getDuplicatedNode(usage);
if (node instanceof CompareNode) {
continue;
}
node.replaceFirstInput(replacementOp, segmentOrigOp);
}
}
}
}
}
if (changed && newStrideNode == null) {
throw GraalError.shouldNotReachHere("Can't find stride node");
}
if (newStrideNode != null) {
// If merge the duplicate code into the loop and remove redundant code
placeNewSegmentAndCleanup(mainCounted, mainLoopBegin, newSegment);
int unrollFactor = mainLoopBegin.getUnrollFactor();
// First restore the old pattern of the loop exit condition so we can update it one way
if (unrollFactor > 1) {
if (compareBound instanceof SubNode) {
SubNode newLimit = (SubNode) compareBound;
ValueNode oldcompareBound = newLimit.getX();
compareNode.replaceFirstInput(newLimit, oldcompareBound);
newLimit.safeDelete();
compareBound = oldcompareBound;
} else if (compareBound instanceof AddNode) {
AddNode newLimit = (AddNode) compareBound;
ValueNode oldcompareBound = newLimit.getX();
compareNode.replaceFirstInput(newLimit, oldcompareBound);
newLimit.safeDelete();
compareBound = oldcompareBound;
}
}
unrollFactor *= 2;
mainLoopBegin.setUnrollFactor(unrollFactor);
// Reset stride to include new segment in loop control.
long oldStride = iv.constantStride() * 2;
// Now update the induction op and the exit condition
if (iv instanceof BasicInductionVariable) {
BasicInductionVariable biv = (BasicInductionVariable) iv;
BinaryArithmeticNode<?> newOp = (BinaryArithmeticNode<?>) newStrideNode;
Stamp strideStamp = newOp.stamp();
ConstantNode newStrideVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, oldStride));
newOp.setY(newStrideVal);
biv.setOP(newOp);
// Now use the current unrollFactor to update the exit condition to power of two
if (unrollFactor > 1) {
if (iv.direction() == Direction.Up) {
int modulas = (unrollFactor - 1);
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, modulas));
ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
compareNode.replaceFirstInput(compareBound, newLimit);
} else if (iv.direction() == Direction.Down) {
int modulas = (unrollFactor - 1);
ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, modulas));
ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
compareNode.replaceFirstInput(compareBound, newLimit);
}
}
mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
}
changed = true;
}
if (changed) {
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
}
return changed;
}
private static Node updateUnrollSegmentValue(PhiNode mainPhiNode, Node inductionNode, Node phiUsage, Node patchNode, LoopFragmentInside newSegment) {
Node node = newSegment.getDuplicatedNode(phiUsage);
assert node != null : phiUsage;
Node replacementOp = null;
int inputCnt = 0;
for (Node input : phiUsage.inputs()) {
inputCnt++;
if (input == mainPhiNode) {
break;
}
}
int newInputCnt = 0;
for (Node input : node.inputs()) {
newInputCnt++;
if (newInputCnt == inputCnt) {
replacementOp = input;
if (mainPhiNode == inductionNode) {
node.replaceFirstInput(input, mainPhiNode);
} else {
node.replaceFirstInput(input, patchNode);
}
break;
}
}
return replacementOp;
}
private static boolean closureOnPhiInputToPhiUse(Node inNode, Node usage, LoopEx loop, StructuredGraph graph) {
NodeWorkList nodes = graph.createNodeWorkList();
nodes.add(inNode);
// Now walk from the inNode to usage if we can find it else we do not have closure
for (Node node : nodes) {
if (node == usage) {
return true;
}
for (Node input : node.inputs()) {
if (!loop.isOutsideLoop(input)) {
if (input != usage) {
nodes.add(input);
} else {
return true;
// For any reason if we have completed a closure, stop processing more
}
}
}
}
return false;
}
private static void placeNewSegmentAndCleanup(CountedLoopInfo mainCounted, LoopBeginNode mainLoopBegin, LoopFragmentInside newSegment) {
// Discard the segment entry and its flow, after if merging it into the loop
StructuredGraph graph = mainLoopBegin.graph();
IfNode loopTest = mainCounted.getLimitTest();
IfNode newSegmentTest = newSegment.getDuplicatedNode(loopTest);
AbstractBeginNode trueSuccessor = loopTest.trueSuccessor();
AbstractBeginNode falseSuccessor = loopTest.falseSuccessor();
FixedNode firstNode;
boolean codeInTrueSide = false;
if (trueSuccessor == mainCounted.getBody()) {
firstNode = trueSuccessor.next();
codeInTrueSide = true;
} else {
assert (falseSuccessor == mainCounted.getBody());
firstNode = falseSuccessor.next();
}
trueSuccessor = newSegmentTest.trueSuccessor();
falseSuccessor = newSegmentTest.falseSuccessor();
for (Node usage : falseSuccessor.anchored().snapshot()) {
usage.replaceFirstInput(falseSuccessor, loopTest.falseSuccessor());
}
for (Node usage : trueSuccessor.anchored().snapshot()) {
usage.replaceFirstInput(trueSuccessor, loopTest.trueSuccessor());
}
AbstractBeginNode startBlockNode;
if (codeInTrueSide) {
startBlockNode = trueSuccessor;
} else {
graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "before");
startBlockNode = falseSuccessor;
}
FixedNode lastNode = getBlockEnd(startBlockNode);
LoopEndNode loopEndNode = getSingleLoopEndFromLoop(mainLoopBegin);
FixedNode lastCodeNode = (FixedNode) loopEndNode.predecessor();
FixedNode newSegmentFirstNode = newSegment.getDuplicatedNode(firstNode);
FixedNode newSegmentLastNode = newSegment.getDuplicatedNode(lastCodeNode);
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment");
if (firstNode instanceof LoopEndNode) {
GraphUtil.killCFG(newSegment.getDuplicatedNode(mainLoopBegin));
} else {
newSegmentLastNode.clearSuccessors();
startBlockNode.setNext(lastNode);
lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode);
newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode);
FixedWithNextNode oldLastNode = (FixedWithNextNode) lastCodeNode;
oldLastNode.setNext(newSegmentFirstNode);
FixedWithNextNode newLastNode = (FixedWithNextNode) newSegmentLastNode;
newLastNode.setNext(loopEndNode);
startBlockNode.clearSuccessors();
lastNode.safeDelete();
Node newSegmentTestStart = newSegmentTest.predecessor();
LogicNode newSegmentIfTest = newSegmentTest.condition();
newSegmentTestStart.clearSuccessors();
newSegmentTest.safeDelete();
newSegmentIfTest.safeDelete();
trueSuccessor.safeDelete();
falseSuccessor.safeDelete();
newSegmentTestStart.safeDelete();
}
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "After placing segment");
}
// This function splits candidate loops into pre, main and post loops,
@ -475,12 +226,12 @@ public abstract class LoopTransformations {
public static void insertPrePostLoops(LoopEx loop, StructuredGraph graph) {
graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
LoopFragmentWhole preLoop = loop.whole();
CountedLoopInfo preCounted = preLoop.loop().counted();
CountedLoopInfo preCounted = loop.counted();
IfNode preLimit = preCounted.getLimitTest();
if (preLimit != null) {
LoopBeginNode preLoopBegin = loop.loopBegin();
InductionVariable preIv = preCounted.getCounter();
LoopExitNode preLoopExitNode = getSingleExitFromLoop(preLoopBegin);
LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
FixedNode continuationNode = preLoopExitNode.next();
// Each duplication is inserted after the original, ergo create the post loop first
@ -497,7 +248,7 @@ public abstract class LoopTransformations {
EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
AbstractMergeNode postMergeNode = postEndNode.merge();
LoopExitNode postLoopExitNode = getSingleExitFromLoop(postLoopBegin);
LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit();
// Update the main loop phi initialization to carry from the pre loop
for (PhiNode prePhiNode : preLoopBegin.phis()) {
@ -511,7 +262,7 @@ public abstract class LoopTransformations {
// In the case of no Bounds tests, we just flow right into the main loop
AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
LoopExitNode mainLoopExitNode = getSingleExitFromLoop(mainLoopBegin);
LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit();
mainLoopExitNode.setNext(mainLandingNode);
preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
@ -528,6 +279,14 @@ public abstract class LoopTransformations {
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)) {
GraphUtil.removeFixedWithUnusedInputs(safepoint);
}
for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
GraphUtil.removeFixedWithUnusedInputs(safepoint);
}
}
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
}
@ -573,21 +332,11 @@ public abstract class LoopTransformations {
}
}
private static LoopExitNode getSingleExitFromLoop(LoopBeginNode curLoopBegin) {
assert curLoopBegin.loopExits().count() == 1;
return curLoopBegin.loopExits().first();
}
private static LoopEndNode getSingleLoopEndFromLoop(LoopBeginNode curLoopBegin) {
assert curLoopBegin.loopEnds().count() == 1;
return curLoopBegin.loopEnds().first();
}
/**
* Find the end of the block following the LoopExit.
*/
private static EndNode getBlockEndAfterLoopExit(LoopBeginNode curLoopBegin) {
FixedNode node = getSingleExitFromLoop(curLoopBegin).next();
FixedNode node = curLoopBegin.getSingleLoopExit().next();
// Find the last node after the exit blocks starts
return getBlockEnd(node);
}
@ -693,43 +442,18 @@ public abstract class LoopTransformations {
}
public static boolean isUnrollableLoop(LoopEx loop) {
if (!loop.isCounted()) {
if (!loop.isCounted() || !loop.counted().getCounter().isConstantStride()) {
return false;
}
LoopBeginNode loopBegin = loop.loopBegin();
boolean isCanonical = 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
// as well.
if (loop.loop().getBlocks().size() < 3) {
isCanonical = true;
return true;
}
}
if (!isCanonical) {
return false;
}
for (ValuePhiNode phi : loopBegin.valuePhis()) {
if (phi.usages().filter(x -> loopBegin.isPhiAtMerge(x)).isNotEmpty()) {
// Filter out Phis which reference Phis at the same merge until the duplication
// logic handles it properly.
return false;
}
InductionVariable iv = loop.getInductionVariables().get(phi);
if (iv == null) {
continue;
}
if (iv instanceof DerivedInductionVariable) {
return false;
} else if (iv instanceof BasicInductionVariable) {
BasicInductionVariable biv = (BasicInductionVariable) iv;
if (!biv.isConstantStride()) {
return false;
}
} else {
return false;
}
}
return true;
return false;
}
}

View File

@ -26,24 +26,34 @@ import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.loop.LoopEx;
import org.graalvm.compiler.loop.LoopsData;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
import org.graalvm.compiler.phases.Phase;
/**
* Rearrange {@link BinaryArithmeticNode#isAssociative() associative binary operations} so that
* invariant parts of the expression can move outside of the loop.
*/
public class ReassociateInvariantPhase extends Phase {
@SuppressWarnings("try")
@Override
protected void run(StructuredGraph graph) {
if (graph.hasLoops()) {
final LoopsData dataReassociate = new LoopsData(graph);
DebugContext debug = graph.getDebug();
try (DebugContext.Scope s = debug.scope("ReassociateInvariants")) {
int iterations = 0;
DebugContext debug = graph.getDebug();
try (DebugContext.Scope s = debug.scope("ReassociateInvariants")) {
boolean changed = true;
while (changed) {
changed = false;
final LoopsData dataReassociate = new LoopsData(graph);
for (LoopEx loop : dataReassociate.loops()) {
loop.reassociateInvariants();
changed |= loop.reassociateInvariants();
}
} catch (Throwable e) {
throw debug.handle(e);
dataReassociate.deleteUnusedNodes();
iterations++;
debug.dump(DebugContext.VERBOSE_LEVEL, graph, "after iteration %d", iterations);
}
dataReassociate.deleteUnusedNodes();
} catch (Throwable e) {
throw debug.handle(e);
}
}
}

View File

@ -26,13 +26,12 @@ import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.graph.iterators.NodeIterable;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.junit.Ignore;
import org.junit.Test;
public class LoopPartialUnrollTest extends GraalCompilerTest {
@Override
protected boolean checkLowTierGraph(StructuredGraph graph) {
protected boolean checkMidTierGraph(StructuredGraph graph) {
NodeIterable<LoopBeginNode> loops = graph.getNodes().filter(LoopBeginNode.class);
for (LoopBeginNode loop : loops) {
if (loop.isMainLoop()) {
@ -45,7 +44,7 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
public static long testMultiplySnippet(int arg) {
long r = 1;
for (int i = 0; branchProbability(0.99, i < arg); i++) {
r *= i;
r += r * i;
}
return r;
}
@ -59,7 +58,24 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
int c = 0;
for (int i = 0; i < d; i++) {
for (int j = 0; branchProbability(0.99, j < i); j++) {
c += j & 0x3;
c += c + j & 0x3;
}
}
return c;
}
@Test
public void testNestedSumBy2() {
for (int i = 0; i < 1000; i++) {
test("testNestedSumBy2Snippet", i);
}
}
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;
@ -75,7 +91,7 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
public static int testSumDownSnippet(int d) {
int c = 0;
for (int j = d; branchProbability(0.99, j > -4); j--) {
c += j & 0x3;
c += c + j & 0x3;
}
return c;
}
@ -83,21 +99,38 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
@Test
public void testSumDown() {
test("testSumDownSnippet", 1);
for (int i = 0; i < 8; i++) {
for (int i = 0; i < 160; i++) {
test("testSumDownSnippet", i);
}
}
@Ignore("Phis which reference the backedge value of other Phis aren't handled properly")
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);
}
}
@Test
public void testLoopCarried() {
test("testLoopCarriedSnippet", 1, 2);
test("testLoopCarriedSnippet", 0, 4);
test("testLoopCarriedSnippet", 4, 0);
}
public static int testLoopCarriedSnippet(int a, int b) {
int c = a;
int d = b;
for (int j = 0; j < a; j++) {
for (int j = 0; branchProbability(0.99, j < a); j++) {
d = c;
c += 1;
}
@ -136,4 +169,16 @@ public class LoopPartialUnrollTest extends GraalCompilerTest {
test("testComplexSnippet", 1000);
}
public static long testSignExtensionSnippet(long arg) {
long r = 1;
for (int i = 0; branchProbability(0.99, i < arg); i++) {
r *= i;
}
return r;
}
@Test
public void testSignExtension() {
test("testSignExtensionSnippet", 9L);
}
}

View File

@ -22,15 +22,13 @@
*/
package org.graalvm.compiler.loop;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Queue;
import jdk.vm.ci.code.BytecodeFrame;
import org.graalvm.compiler.core.common.calc.Condition;
import org.graalvm.compiler.core.common.cfg.Loop;
import org.graalvm.compiler.core.common.type.IntegerStamp;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Graph;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeBitMap;
import org.graalvm.compiler.graph.iterators.NodePredicate;
@ -72,7 +70,9 @@ import org.graalvm.util.EconomicMap;
import org.graalvm.util.EconomicSet;
import org.graalvm.util.Equivalence;
import jdk.vm.ci.code.BytecodeFrame;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Queue;
public class LoopEx {
private final Loop<Block> loop;
@ -164,33 +164,46 @@ public class LoopEx {
private class InvariantPredicate implements NodePredicate {
private final Graph.Mark mark;
InvariantPredicate() {
this.mark = loopBegin().graph().getMark();
}
@Override
public boolean apply(Node n) {
if (loopBegin().graph().isNew(mark, n)) {
// Newly created nodes are unknown.
return false;
}
return isOutsideLoop(n);
}
}
public void reassociateInvariants() {
InvariantPredicate invariant = new InvariantPredicate();
public boolean reassociateInvariants() {
int count = 0;
StructuredGraph graph = loopBegin().graph();
InvariantPredicate invariant = new InvariantPredicate();
for (BinaryArithmeticNode<?> binary : whole().nodes().filter(BinaryArithmeticNode.class)) {
if (!binary.isAssociative()) {
continue;
}
ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY());
if (result != binary) {
DebugContext debug = graph.getDebug();
if (debug.isLogEnabled()) {
debug.log("%s : Reassociated %s into %s", graph.method().format("%H::%n"), binary, result);
}
if (!result.isAlive()) {
assert !result.isDeleted();
result = graph.addOrUniqueWithInputs(result);
}
DebugContext debug = graph.getDebug();
if (debug.isLogEnabled()) {
debug.log("%s : Reassociated %s into %s", graph.method().format("%H::%n"), binary, result);
}
binary.replaceAtUsages(result);
GraphUtil.killWithUnusedFloatingInputs(binary);
count++;
}
}
return count != 0;
}
public boolean detectCounted() {

View File

@ -22,11 +22,7 @@
*/
package org.graalvm.compiler.loop;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import jdk.vm.ci.meta.TriState;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Graph;
import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
@ -57,7 +53,10 @@ import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
import org.graalvm.util.EconomicMap;
import jdk.vm.ci.meta.TriState;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
public abstract class LoopFragment {
@ -78,7 +77,10 @@ public abstract class LoopFragment {
this.nodesReady = false;
}
public LoopEx loop() {
/**
* Return the original LoopEx for this fragment. For duplicated fragments this returns null.
*/
protected LoopEx loop() {
return loop;
}
@ -172,6 +174,8 @@ public abstract class LoopFragment {
NodeIterable<Node> nodesIterable = original().nodes();
duplicationMap = graph().addDuplicates(nodesIterable, graph(), nodesIterable.count(), dr);
finishDuplication();
nodes = new NodeBitMap(graph());
nodes.markAll(duplicationMap.getValues());
nodesReady = true;
} else {
// TODO (gd) apply fix ?

View File

@ -22,9 +22,7 @@
*/
package org.graalvm.compiler.loop;
import java.util.LinkedList;
import java.util.List;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
import org.graalvm.compiler.graph.Node;
@ -34,25 +32,37 @@ import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.AbstractEndNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.EndNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.GuardPhiNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.LoopEndNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.MergeNode;
import org.graalvm.compiler.nodes.PhiNode;
import org.graalvm.compiler.nodes.ProxyNode;
import org.graalvm.compiler.nodes.SafepointNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.ValuePhiNode;
import org.graalvm.compiler.nodes.VirtualState.NodeClosure;
import org.graalvm.compiler.nodes.calc.AddNode;
import org.graalvm.compiler.nodes.calc.CompareNode;
import org.graalvm.compiler.nodes.calc.SubNode;
import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.util.EconomicMap;
import org.graalvm.util.Equivalence;
import java.util.LinkedList;
import java.util.List;
public class LoopFragmentInside extends LoopFragment {
/**
@ -133,9 +143,126 @@ public class LoopFragmentInside extends LoopFragment {
}
public void insertWithinAfter(LoopEx loop) {
assert this.isDuplicate() && this.original().loop() == loop;
assert isDuplicate() && original().loop() == loop;
patchNodes(dataFixWithinAfter);
LoopBeginNode mainLoopBegin = loop.loopBegin();
for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1));
if (duplicatedNode != null) {
mainPhiNode.setValueAt(1, duplicatedNode);
} else {
assert mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1));
}
}
placeNewSegmentAndCleanup(loop);
// Remove any safepoints from the original copy leaving only the duplicated one
assert loop.whole().nodes().filter(SafepointNode.class).count() == nodes().filter(SafepointNode.class).count();
for (SafepointNode safepoint : loop.whole().nodes().filter(SafepointNode.class)) {
GraphUtil.removeFixedWithUnusedInputs(safepoint);
}
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);
}
mainLoopBegin.setUnrollFactor(unrollFactor * 2);
mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
mainLoopBegin.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "After insertWithinAfter %s", mainLoopBegin);
}
private void placeNewSegmentAndCleanup(LoopEx loop) {
CountedLoopInfo mainCounted = loop.counted();
LoopBeginNode mainLoopBegin = loop.loopBegin();
// Discard the segment entry and its flow, after if merging it into the loop
StructuredGraph graph = mainLoopBegin.graph();
IfNode loopTest = mainCounted.getLimitTest();
IfNode newSegmentTest = getDuplicatedNode(loopTest);
AbstractBeginNode trueSuccessor = loopTest.trueSuccessor();
AbstractBeginNode falseSuccessor = loopTest.falseSuccessor();
FixedNode firstNode;
boolean codeInTrueSide = false;
if (trueSuccessor == mainCounted.getBody()) {
firstNode = trueSuccessor.next();
codeInTrueSide = true;
} else {
assert (falseSuccessor == mainCounted.getBody());
firstNode = falseSuccessor.next();
}
trueSuccessor = newSegmentTest.trueSuccessor();
falseSuccessor = newSegmentTest.falseSuccessor();
for (Node usage : falseSuccessor.anchored().snapshot()) {
usage.replaceFirstInput(falseSuccessor, loopTest.falseSuccessor());
}
for (Node usage : trueSuccessor.anchored().snapshot()) {
usage.replaceFirstInput(trueSuccessor, loopTest.trueSuccessor());
}
AbstractBeginNode startBlockNode;
if (codeInTrueSide) {
startBlockNode = trueSuccessor;
} else {
graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "before");
startBlockNode = falseSuccessor;
}
FixedNode lastNode = getBlockEnd(startBlockNode);
LoopEndNode loopEndNode = mainLoopBegin.getSingleLoopEnd();
FixedWithNextNode lastCodeNode = (FixedWithNextNode) loopEndNode.predecessor();
FixedNode newSegmentFirstNode = getDuplicatedNode(firstNode);
FixedWithNextNode newSegmentLastNode = getDuplicatedNode(lastCodeNode);
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment");
if (firstNode instanceof LoopEndNode) {
GraphUtil.killCFG(getDuplicatedNode(mainLoopBegin));
} else {
newSegmentLastNode.clearSuccessors();
startBlockNode.setNext(lastNode);
lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode);
newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode);
lastCodeNode.setNext(newSegmentFirstNode);
newSegmentLastNode.setNext(loopEndNode);
startBlockNode.clearSuccessors();
lastNode.safeDelete();
Node newSegmentTestStart = newSegmentTest.predecessor();
LogicNode newSegmentIfTest = newSegmentTest.condition();
newSegmentTestStart.clearSuccessors();
newSegmentTest.safeDelete();
newSegmentIfTest.safeDelete();
trueSuccessor.safeDelete();
falseSuccessor.safeDelete();
newSegmentTestStart.safeDelete();
}
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "After placing segment");
}
private static EndNode getBlockEnd(FixedNode node) {
FixedNode curNode = node;
while (curNode instanceof FixedWithNextNode) {
curNode = ((FixedWithNextNode) curNode).next();
}
return (EndNode) curNode;
}
@Override

View File

@ -300,6 +300,16 @@ public final class LoopBeginNode extends AbstractMergeNode implements IterableNo
return begin instanceof LoopExitNode && ((LoopExitNode) begin).loopBegin() == this;
}
public LoopExitNode getSingleLoopExit() {
assert loopExits().count() == 1;
return loopExits().first();
}
public LoopEndNode getSingleLoopEnd() {
assert loopEnds().count() == 1;
return loopEnds().first();
}
public void removeExits() {
for (LoopExitNode loopexit : loopExits().snapshot()) {
loopexit.removeProxies();

View File

@ -35,7 +35,7 @@ import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
import org.graalvm.compiler.nodes.spi.ValueProxy;
@NodeInfo(cycles = CYCLES_0, size = SIZE_0)
public final class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowerable, ValueProxy, GuardingNode {
public class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowerable, ValueProxy, GuardingNode {
public static final NodeClass<FixedValueAnchorNode> TYPE = NodeClass.create(FixedValueAnchorNode.class);
@Input ValueNode object;
@ -45,11 +45,15 @@ public final class FixedValueAnchorNode extends FixedWithNextNode implements LIR
return object;
}
public FixedValueAnchorNode(ValueNode object) {
super(TYPE, object.stamp());
protected FixedValueAnchorNode(NodeClass<? extends FixedValueAnchorNode> c, ValueNode object) {
super(c, object.stamp());
this.object = object;
}
public FixedValueAnchorNode(ValueNode object) {
this(TYPE, object);
}
public FixedValueAnchorNode(ValueNode object, Stamp predefinedStamp) {
super(TYPE, predefinedStamp);
this.object = object;

View File

@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.spi.Lowerable;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.nodes.spi.Virtualizable;
import org.graalvm.compiler.nodes.spi.VirtualizerTool;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
import org.graalvm.word.LocationIdentity;
@ -124,7 +125,7 @@ public final class RawStoreNode extends UnsafeAccessNode implements StateSplit,
int entryIndex = virtual.entryIndexForOffset(off, accessKind());
if (entryIndex != -1) {
JavaKind entryKind = virtual.entryKind(entryIndex);
boolean canVirtualize = entryKind == accessKind() || entryKind == accessKind().getStackKind();
boolean canVirtualize = entryKind == accessKind() || (entryKind == accessKind().getStackKind() && !StampTool.typeOrNull(object()).isArray());
if (!canVirtualize) {
/*
* Special case: If the entryKind is long, allow arbitrary kinds as long as

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2011, 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.nodes.extended;
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.graph.spi.Canonicalizable;
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
import org.graalvm.compiler.nodeinfo.InputType;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.ValueNode;
/**
* This node provides a state split along with the functionality of {@link FixedValueAnchorNode}.
*/
@NodeInfo(cycles = CYCLES_0, size = SIZE_0)
public final class StateSplitProxyNode extends FixedValueAnchorNode implements Canonicalizable, StateSplit {
public static final NodeClass<StateSplitProxyNode> TYPE = NodeClass.create(StateSplitProxyNode.class);
@OptionalInput(InputType.State) FrameState stateAfter;
@Override
public FrameState stateAfter() {
return stateAfter;
}
@Override
public void setStateAfter(FrameState x) {
assert x == null || x.isAlive() : "frame state must be in a graph";
updateUsages(stateAfter, x);
stateAfter = x;
}
@Override
public boolean hasSideEffect() {
return true;
}
public StateSplitProxyNode(ValueNode object) {
super(TYPE, object);
}
@Override
public Node canonical(CanonicalizerTool tool) {
if (object.isConstant()) {
return object;
}
return this;
}
}

View File

@ -1092,7 +1092,7 @@ public class InvocationPlugins {
static final Class<?>[][] SIGS;
static {
if (!Assertions.ENABLED) {
if (!Assertions.assertionsEnabled()) {
throw new GraalError("%s must only be used in assertions", Checks.class.getName());
}
ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);

View File

@ -90,7 +90,7 @@ public class LoadIndexedNode extends AccessIndexedNode implements Virtualizable,
@Override
public boolean inferStamp() {
return updateStamp(createStamp(graph().getAssumptions(), array(), elementKind()));
return updateStamp(stamp.improveWith(createStamp(graph().getAssumptions(), array(), elementKind())));
}
@Override
@ -101,7 +101,13 @@ public class LoadIndexedNode extends AccessIndexedNode implements Virtualizable,
ValueNode indexValue = tool.getAlias(index());
int idx = indexValue.isConstant() ? indexValue.asJavaConstant().asInt() : -1;
if (idx >= 0 && idx < virtual.entryCount()) {
tool.replaceWith(tool.getEntry(virtual, idx));
ValueNode entry = tool.getEntry(virtual, idx);
if (stamp.isCompatible(entry.stamp())) {
tool.replaceWith(entry);
} else {
assert stamp().getStackKind() == JavaKind.Int && (entry.stamp().getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double ||
entry.getStackKind() == JavaKind.Illegal) : "Can only allow different stack kind two slot marker writes on one stot fields.";
}
}
}
}

View File

@ -28,10 +28,12 @@
package org.graalvm.compiler.options.test;
import static org.graalvm.compiler.options.OptionValues.asMap;
import static org.graalvm.compiler.options.test.TestOptionKey.Options.MyOption;
import static org.graalvm.compiler.options.test.TestOptionKey.Options.MyOtherOption;
import static org.junit.Assert.assertEquals;
import org.graalvm.compiler.options.ModifiableOptionValues;
import org.graalvm.compiler.options.OptionDescriptor;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionValues;
@ -65,4 +67,20 @@ public class TestOptionKey {
}
Assert.assertTrue(sawAssertionError);
}
/**
* Tests that initial values are properly copied.
*/
@Test
public void testDerived() {
OptionValues initialOptions = new ModifiableOptionValues(asMap(MyOption, "new value 1"));
OptionValues derivedOptions = new OptionValues(initialOptions, MyOtherOption, "ignore");
Assert.assertEquals("new value 1", MyOption.getValue(derivedOptions));
initialOptions = new OptionValues(asMap(MyOption, "new value 1"));
derivedOptions = new OptionValues(initialOptions, MyOtherOption, "ignore");
Assert.assertEquals("new value 1", MyOption.getValue(derivedOptions));
}
}

View File

@ -47,8 +47,17 @@ public class ModifiableOptionValues extends OptionValues {
v.set(map);
}
/**
* Value that can be used in {@link #update(UnmodifiableEconomicMap)} and
* {@link #update(OptionKey, Object)} to remove an explicitly set value for a key such that
* {@link OptionKey#hasBeenSet(OptionValues)} will return {@code false} for the key.
*/
public static final Object UNSET_KEY = new Object();
/**
* Updates this object with the given key/value pair.
*
* @see #UNSET_KEY
*/
public void update(OptionKey<?> key, Object value) {
UnmodifiableEconomicMap<OptionKey<?>, Object> expect;
@ -56,14 +65,20 @@ public class ModifiableOptionValues extends OptionValues {
do {
expect = v.get();
newMap = EconomicMap.create(Equivalence.IDENTITY, expect);
key.update(newMap, value);
// Need to do the null encoding here as `key.update()` doesn't do it
newMap.put(key, encodeNull(value));
if (value == UNSET_KEY) {
newMap.removeKey(key);
} else {
key.update(newMap, value);
// Need to do the null encoding here as `key.update()` doesn't do it
newMap.put(key, encodeNull(value));
}
} while (!v.compareAndSet(expect, newMap));
}
/**
* Updates this object with the key/value pairs in {@code values}.
*
* @see #UNSET_KEY
*/
public void update(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
if (values.isEmpty()) {
@ -78,9 +93,13 @@ public class ModifiableOptionValues extends OptionValues {
while (cursor.advance()) {
OptionKey<?> key = cursor.getKey();
Object value = cursor.getValue();
key.update(newMap, value);
// Need to do the null encoding here as `key.update()` doesn't do it
newMap.put(key, encodeNull(value));
if (value == UNSET_KEY) {
newMap.removeKey(key);
} else {
key.update(newMap, value);
// Need to do the null encoding here as `key.update()` doesn't do it
newMap.put(key, encodeNull(value));
}
}
} while (!v.compareAndSet(expect, newMap));
}

View File

@ -52,7 +52,7 @@ public class OptionValues {
public OptionValues(OptionValues initialValues, UnmodifiableEconomicMap<OptionKey<?>, Object> extraPairs) {
EconomicMap<OptionKey<?>, Object> map = newOptionMap();
if (initialValues != null) {
map.putAll(initialValues.values);
map.putAll(initialValues.getMap());
}
initMap(map, extraPairs);
this.values = map;

View File

@ -101,15 +101,12 @@ public class ExpandLogicPhase extends Phase {
private static void processIf(LogicNode x, boolean xNegated, LogicNode y, boolean yNegated, IfNode ifNode, double shortCircuitProbability) {
AbstractBeginNode trueTarget = ifNode.trueSuccessor();
AbstractBeginNode falseTarget = ifNode.falseSuccessor();
double firstIfProbability = shortCircuitProbability;
/*
* P(Y | not(X)) = P(Y inter not(X)) / P(not(X)) = (P(X union Y) - P(X)) / (1 - P(X))
*
* P(X) = shortCircuitProbability
*
* P(X union Y) = ifNode.probability(trueTarget)
*/
double secondIfProbability = (ifNode.probability(trueTarget) - shortCircuitProbability) / (1 - shortCircuitProbability);
// while the first if node is reached by all cases, the true values are split between the
// first and the second if
double firstIfProbability = ifNode.probability(trueTarget) * shortCircuitProbability;
// the second if node is reached by a reduced number of true cases but the same number of
// false cases
double secondIfProbability = 1 - ifNode.probability(falseTarget) / (1 - firstIfProbability);
secondIfProbability = Math.min(1.0, Math.max(0.0, secondIfProbability));
if (Double.isNaN(secondIfProbability)) {
secondIfProbability = 0.5;

View File

@ -35,6 +35,7 @@ import org.graalvm.compiler.nodes.AbstractDeoptimizeNode;
import org.graalvm.compiler.nodes.AbstractEndNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.CompressionNode;
import org.graalvm.compiler.nodes.DeoptimizeNode;
import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode;
import org.graalvm.compiler.nodes.DynamicDeoptimizeNode;
@ -197,6 +198,14 @@ public class UseTrappingNullChecksPhase extends BasePhase<LowTierContext> {
AddressNode address = fixedAccessNode.getAddress();
ValueNode base = address.getBase();
ValueNode index = address.getIndex();
// allow for architectures which cannot fold an
// intervening uncompress out of the address chain
if (base != null && base instanceof CompressionNode) {
base = ((CompressionNode) base).getValue();
}
if (index != null && index instanceof CompressionNode) {
index = ((CompressionNode) index).getValue();
}
if (((base == value && index == null) || (base == null && index == value)) && address.getMaxConstantDisplacement() < implicitNullCheckLimit) {
// Opportunity for implicit null check as part of an existing read found!
fixedAccessNode.setStateBefore(deopt.stateBefore());

View File

@ -86,23 +86,23 @@ public abstract class BasePhase<C> implements PhaseSizeContract {
public static class BasePhaseStatistics {
/**
* Records time spent in {@link #apply(StructuredGraph, Object, boolean)}.
* Records time spent in {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
*/
private final TimerKey timer;
/**
* Counts calls to {@link #apply(StructuredGraph, Object, boolean)}.
* Counts calls to {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
*/
private final CounterKey executionCount;
/**
* Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to
* {@link #apply(StructuredGraph, Object, boolean)}.
* {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
*/
private final CounterKey inputNodesCount;
/**
* Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}.
* Records memory usage within {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
*/
private final MemUseTrackerKey memUseTracker;

View File

@ -30,7 +30,6 @@ import java.util.Arrays;
import java.util.Formatter;
import java.util.List;
import org.graalvm.compiler.core.common.GraalOptions;
import org.graalvm.compiler.core.common.SuppressFBWarnings;
import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
import org.graalvm.compiler.core.common.cfg.BlockMap;
@ -107,7 +106,7 @@ public final class SchedulePhase extends Phase {
}
private NodeEventScope verifyImmutableGraph(StructuredGraph graph) {
if (immutableGraph && Assertions.ENABLED) {
if (immutableGraph && Assertions.assertionsEnabled()) {
return graph.trackNodeEvents(new NodeEventListener() {
@Override
public void event(NodeEvent e, Node node) {
@ -178,7 +177,7 @@ public final class SchedulePhase extends Phase {
sortNodesLatestWithinBlock(cfg, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
assert verifySchedule(cfg, latestBlockToNodesMap, currentNodeMap);
assert (!GraalOptions.DetailedAsserts.getValue(graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
assert (!Assertions.detailedAssertionsEnabled(graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
this.blockToNodesMap = latestBlockToNodesMap;
@ -880,7 +879,7 @@ public final class SchedulePhase extends Phase {
}
}
assert (!GraalOptions.DetailedAsserts.getValue(cfg.graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
assert (!Assertions.detailedAssertionsEnabled(cfg.graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
}
private static void processStackPhi(NodeStack stack, PhiNode phiNode, NodeMap<MicroBlock> nodeToBlock, NodeBitMap visited) {

View File

@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.core.common.CompilationIdentifier;
import org.graalvm.compiler.debug.Assertions;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugDumpHandler;
import org.graalvm.compiler.debug.DebugHandler;
@ -203,7 +204,7 @@ public class GraalDebugHandlersFactory implements DebugHandlersFactory {
}
String ext = PathUtilities.formatExtension(extension);
Path result = createUnique(DebugOptions.getDumpDirectory(options), id, label, ext, createDirectory);
if (ShowDumpFiles.getValue(options)) {
if (ShowDumpFiles.getValue(options) || Assertions.assertionsEnabled()) {
TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString());
}
return result;

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.replacements.aarch64;
import jdk.vm.ci.aarch64.AArch64Kind;
import org.graalvm.compiler.core.aarch64.AArch64ArithmeticLIRGenerator;
import org.graalvm.compiler.core.aarch64.AArch64LIRGenerator;
import org.graalvm.compiler.core.common.type.IntegerStamp;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.SignExtendNode;
import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
import org.graalvm.compiler.nodes.extended.GuardingNode;
import org.graalvm.compiler.nodes.memory.ReadNode;
import org.graalvm.compiler.nodes.memory.address.AddressNode;
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
import org.graalvm.word.LocationIdentity;
/**
* AArch64-specific subclass of ReadNode that knows how to merge ZeroExtend and SignExtend into the
* read.
*/
@NodeInfo
public class AArch64ReadNode extends ReadNode {
public static final NodeClass<AArch64ReadNode> TYPE = NodeClass.create(AArch64ReadNode.class);
private final IntegerStamp accessStamp;
private final boolean isSigned;
public AArch64ReadNode(AddressNode address, LocationIdentity location, Stamp stamp, GuardingNode guard, BarrierType barrierType, boolean nullCheck,
FrameState stateBefore, IntegerStamp accessStamp, boolean isSigned) {
super(TYPE, address, location, stamp, guard, barrierType, nullCheck, stateBefore);
this.accessStamp = accessStamp;
this.isSigned = isSigned;
}
@Override
public void generate(NodeLIRBuilderTool gen) {
AArch64LIRGenerator lirgen = (AArch64LIRGenerator) gen.getLIRGeneratorTool();
AArch64ArithmeticLIRGenerator arithgen = (AArch64ArithmeticLIRGenerator) lirgen.getArithmetic();
AArch64Kind readKind = (AArch64Kind) lirgen.getLIRKind(accessStamp).getPlatformKind();
int resultBits = ((IntegerStamp) stamp()).getBits();
gen.setResult(this, arithgen.emitExtendMemory(isSigned, readKind, resultBits, (AArch64AddressValue) gen.operand(getAddress()), gen.state(this)));
}
/**
* replace a ReadNode with an AArch64-specific variant which knows how to merge a downstream
* zero or sign extend into the read operation.
*
* @param readNode
*/
public static void replace(ReadNode readNode) {
assert readNode.getUsageCount() == 1;
assert readNode.getUsageAt(0) instanceof ZeroExtendNode || readNode.getUsageAt(0) instanceof SignExtendNode;
ValueNode usage = (ValueNode) readNode.getUsageAt(0);
boolean isSigned = usage instanceof SignExtendNode;
IntegerStamp accessStamp = ((IntegerStamp) readNode.getAccessStamp());
AddressNode address = readNode.getAddress();
LocationIdentity location = readNode.getLocationIdentity();
Stamp stamp = usage.stamp();
GuardingNode guard = readNode.getGuard();
BarrierType barrierType = readNode.getBarrierType();
boolean nullCheck = readNode.getNullCheck();
FrameState stateBefore = readNode.stateBefore();
AArch64ReadNode clone = new AArch64ReadNode(address, location, stamp, guard, barrierType, nullCheck, stateBefore, accessStamp, isSigned);
StructuredGraph graph = readNode.graph();
graph.add(clone);
// splice out the extend node
usage.replaceAtUsagesAndDelete(readNode);
// swap the clone for the read
graph.replaceFixedWithFixed(readNode, clone);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.replacements.aarch64;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.calc.SignExtendNode;
import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
import org.graalvm.compiler.nodes.memory.ReadNode;
import org.graalvm.compiler.phases.Phase;
/**
* AArch64-specific phase which substitutes certain read nodes with arch-specific variants in order
* to allow merging of zero and sign extension into the read operation.
*/
public class AArch64ReadReplacementPhase extends Phase {
@Override
protected void run(StructuredGraph graph) {
for (Node node : graph.getNodes()) {
// don't process nodes we just added
if (node instanceof AArch64ReadNode) {
continue;
}
if (node instanceof ReadNode) {
ReadNode readNode = (ReadNode) node;
if (readNode.getUsageCount() == 1) {
Node usage = readNode.getUsageAt(0);
if (usage instanceof ZeroExtendNode || usage instanceof SignExtendNode) {
AArch64ReadNode.replace(readNode);
}
}
}
}
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2011, 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 org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.junit.Test;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.meta.ResolvedJavaMethod;
/**
* Tests that deoptimization upon volatile read will not roll back the read. The test cases rely on
* the fact that invocations to {@link GraalDirectives} utilities are substituted and not valid
* targets for deoptimization.
*/
public class DeoptimizeOnVolatileReadTest extends GraalCompilerTest {
static class Dummy {
boolean f1 = false;
volatile boolean f2 = false;
}
public static int test1Snippet(Dummy dummy) {
if (GraalDirectives.injectBranchProbability(0, GraalDirectives.inCompiledCode() & dummy.f1)) {
return 1;
}
return 0;
}
@Test
public void test1() {
ResolvedJavaMethod method = getResolvedJavaMethod("test1Snippet");
Dummy dummy = new Dummy();
Result expected = executeExpected(method, null, dummy);
assertEquals(new Result(0, null), expected);
dummy.f1 = true;
InstalledCode code = getCode(method);
Result actual;
try {
actual = new Result(code.executeVarargs(dummy), null);
} catch (Exception e) {
actual = new Result(null, e);
}
// The code should get deoptimized, and resume execution at the beginning of the method.
// Therefore it does not re-enter the branch as inCompiledCode() is false.
assertEquals(new Result(0, null), actual);
assertFalse(code.isValid());
}
public static int test2Snippet(Dummy dummy) {
if (GraalDirectives.injectBranchProbability(0, GraalDirectives.inCompiledCode() & dummy.f2)) {
return 1;
}
return 0;
}
@Test
public void test2() {
ResolvedJavaMethod method = getResolvedJavaMethod("test2Snippet");
Dummy dummy = new Dummy();
Result expected = executeExpected(method, null, dummy);
assertEquals(new Result(0, null), expected);
dummy.f2 = true;
InstalledCode code = getCode(method);
Result actual;
try {
actual = new Result(code.executeVarargs(dummy), null);
} catch (Exception e) {
actual = new Result(null, e);
}
// The code should get deoptimized, and resume execution at the then-branch.
assertEquals(new Result(1, null), actual);
assertFalse(code.isValid());
}
}

View File

@ -31,7 +31,7 @@ public final class ReplacementsUtil {
// empty
}
public static final boolean REPLACEMENTS_ASSERTIONS_ENABLED = Assertions.ENABLED;
public static final boolean REPLACEMENTS_ASSERTIONS_ENABLED = Assertions.assertionsEnabled();
/**
* Asserts that condition evaluates to true by the time compilation is finished. This is

View File

@ -320,6 +320,7 @@ public final class MethodHandleNode extends MacroStateSplitNode implements Simpl
ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
if (argumentType == null || (argumentType.isAssignableFrom(targetType.getType()) && !argumentType.equals(targetType.getType()))) {
LogicNode inst = InstanceOfNode.createAllowNull(targetType, argument, null, null);
assert !inst.isAlive();
if (!inst.isTautology()) {
inst = adder.add(inst);
AnchoringNode guardAnchor = adder.getGuardAnchor();
@ -337,8 +338,6 @@ public final class MethodHandleNode extends MacroStateSplitNode implements Simpl
}
ValueNode valueNode = adder.add(PiNode.create(argument, StampFactory.object(targetType), guard.asNode()));
arguments[index] = valueNode;
} else {
inst.safeDelete();
}
}
}

View File

@ -36,6 +36,8 @@ import org.graalvm.util.EconomicMap;
import org.graalvm.util.Equivalence;
import org.graalvm.word.LocationIdentity;
import jdk.vm.ci.meta.JavaKind;
public final class PEReadEliminationBlockState extends PartialEscapeBlockState<PEReadEliminationBlockState> {
final EconomicMap<ReadCacheEntry, ValueNode> readCache;
@ -45,17 +47,20 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
public final LocationIdentity identity;
public final ValueNode object;
public final int index;
public final JavaKind kind;
ReadCacheEntry(LocationIdentity identity, ValueNode object, int index) {
ReadCacheEntry(LocationIdentity identity, ValueNode object, int index, JavaKind kind) {
this.identity = identity;
this.object = object;
this.index = index;
this.kind = kind;
}
@Override
public int hashCode() {
int result = 31 + ((identity == null) ? 0 : identity.hashCode());
result = 31 * result + ((object == null) ? 0 : System.identityHashCode(object));
result = 31 * result + kind.ordinal();
return result * 31 + index;
}
@ -65,12 +70,12 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
return false;
}
ReadCacheEntry other = (ReadCacheEntry) obj;
return identity.equals(other.identity) && object == other.object && index == other.index;
return identity.equals(other.identity) && object == other.object && index == other.index && kind == other.kind;
}
@Override
public String toString() {
return index == -1 ? (object + ":" + identity) : (object + "[" + index + "]:" + identity);
return index == -1 ? (object + ":" + kind + "<" + identity + ">") : (object + "[" + index + "]:" + kind + "<" + identity + ">");
}
}
@ -94,7 +99,7 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
if (virtual instanceof VirtualInstanceNode) {
VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
for (int i = 0; i < instance.entryCount(); i++) {
readCache.put(new ReadCacheEntry(new FieldLocationIdentity(instance.field(i)), representation, -1), values.get(i));
readCache.put(new ReadCacheEntry(new FieldLocationIdentity(instance.field(i)), representation, -1, instance.field(i).getJavaKind()), values.get(i));
}
}
}
@ -107,7 +112,7 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
return super.equivalentTo(other);
}
public void addReadCache(ValueNode object, LocationIdentity identity, int index, ValueNode value, PartialEscapeClosure<?> closure) {
public void addReadCache(ValueNode object, LocationIdentity identity, int index, JavaKind kind, ValueNode value, PartialEscapeClosure<?> closure) {
ValueNode cacheObject;
ObjectState obj = closure.getObjectState(this, object);
if (obj != null) {
@ -116,10 +121,10 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
} else {
cacheObject = object;
}
readCache.put(new ReadCacheEntry(identity, cacheObject, index), value);
readCache.put(new ReadCacheEntry(identity, cacheObject, index, kind), value);
}
public ValueNode getReadCache(ValueNode object, LocationIdentity identity, int index, PartialEscapeClosure<?> closure) {
public ValueNode getReadCache(ValueNode object, LocationIdentity identity, int index, JavaKind kind, PartialEscapeClosure<?> closure) {
ValueNode cacheObject;
ObjectState obj = closure.getObjectState(this, object);
if (obj != null) {
@ -128,7 +133,7 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
} else {
cacheObject = object;
}
ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject, index));
ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject, index, kind));
obj = closure.getObjectState(this, cacheValue);
if (obj != null) {
assert !obj.isVirtual();

View File

@ -31,6 +31,7 @@ import java.util.List;
import org.graalvm.compiler.core.common.cfg.Loop;
import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.FieldLocationIdentity;
@ -130,9 +131,9 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
return false;
}
private boolean processStore(FixedNode store, ValueNode object, LocationIdentity identity, int index, ValueNode value, PEReadEliminationBlockState state, GraphEffectList effects) {
private boolean processStore(FixedNode store, ValueNode object, LocationIdentity identity, int index, JavaKind kind, ValueNode value, PEReadEliminationBlockState state, GraphEffectList effects) {
ValueNode unproxiedObject = GraphUtil.unproxify(object);
ValueNode cachedValue = state.getReadCache(object, identity, index, this);
ValueNode cachedValue = state.getReadCache(object, identity, index, kind, this);
ValueNode finalValue = getScalarAlias(value);
boolean result = false;
@ -141,15 +142,17 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
result = true;
}
state.killReadCache(identity, index);
state.addReadCache(unproxiedObject, identity, index, finalValue, this);
state.addReadCache(unproxiedObject, identity, index, kind, finalValue, this);
return result;
}
private boolean processLoad(FixedNode load, ValueNode object, LocationIdentity identity, int index, PEReadEliminationBlockState state, GraphEffectList effects) {
private boolean processLoad(FixedNode load, ValueNode object, LocationIdentity identity, int index, JavaKind kind, PEReadEliminationBlockState state, GraphEffectList effects) {
ValueNode unproxiedObject = GraphUtil.unproxify(object);
ValueNode cachedValue = state.getReadCache(unproxiedObject, identity, index, this);
ValueNode cachedValue = state.getReadCache(unproxiedObject, identity, index, kind, this);
if (cachedValue != null) {
if (!load.stamp().isCompatible(cachedValue.stamp())) {
Stamp loadStamp = load.stamp();
Stamp cachedValueStamp = cachedValue.stamp();
if (!loadStamp.isCompatible(cachedValueStamp)) {
/*
* Can either be the first field of a two slot write to a one slot field which would
* have a non compatible stamp or the second load which will see Illegal.
@ -164,7 +167,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
return true;
}
} else {
state.addReadCache(unproxiedObject, identity, index, load, this);
state.addReadCache(unproxiedObject, identity, index, kind, load, this);
return false;
}
}
@ -177,13 +180,13 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
int index = VirtualArrayNode.entryIndexForOffset(offset, load.accessKind(), type.getComponentType(), Integer.MAX_VALUE);
ValueNode object = GraphUtil.unproxify(load.object());
LocationIdentity location = NamedLocationIdentity.getArrayLocation(type.getComponentType().getJavaKind());
ValueNode cachedValue = state.getReadCache(object, location, index, this);
ValueNode cachedValue = state.getReadCache(object, location, index, load.accessKind(), this);
if (cachedValue != null && load.stamp().isCompatible(cachedValue.stamp())) {
effects.replaceAtUsages(load, cachedValue, load);
addScalarAlias(load, cachedValue);
return true;
} else {
state.addReadCache(object, location, index, load, this);
state.addReadCache(object, location, index, load.accessKind(), load, this);
}
}
}
@ -197,7 +200,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
if (store.offset().isConstant()) {
long offset = store.offset().asJavaConstant().asLong();
int index = VirtualArrayNode.entryIndexForOffset(offset, store.accessKind(), type.getComponentType(), Integer.MAX_VALUE);
return processStore(store, store.object(), location, index, store.value(), state, effects);
return processStore(store, store.object(), location, index, store.accessKind(), store.value(), state, effects);
} else {
processIdentity(state, location);
}
@ -208,7 +211,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
}
private boolean processArrayLength(ArrayLengthNode length, PEReadEliminationBlockState state, GraphEffectList effects) {
return processLoad(length, length.array(), ARRAY_LENGTH_LOCATION, -1, state, effects);
return processLoad(length, length.array(), ARRAY_LENGTH_LOCATION, -1, JavaKind.Int, state, effects);
}
private boolean processStoreField(StoreFieldNode store, PEReadEliminationBlockState state, GraphEffectList effects) {
@ -216,7 +219,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
state.killReadCache();
return false;
}
return processStore(store, store.object(), new FieldLocationIdentity(store.field()), -1, store.value(), state, effects);
return processStore(store, store.object(), new FieldLocationIdentity(store.field()), -1, store.field().getJavaKind(), store.value(), state, effects);
}
private boolean processLoadField(LoadFieldNode load, PEReadEliminationBlockState state, GraphEffectList effects) {
@ -224,14 +227,14 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
state.killReadCache();
return false;
}
return processLoad(load, load.object(), new FieldLocationIdentity(load.field()), -1, state, effects);
return processLoad(load, load.object(), new FieldLocationIdentity(load.field()), -1, load.field().getJavaKind(), state, effects);
}
private boolean processStoreIndexed(StoreIndexedNode store, PEReadEliminationBlockState state, GraphEffectList effects) {
LocationIdentity arrayLocation = NamedLocationIdentity.getArrayLocation(store.elementKind());
if (store.index().isConstant()) {
int index = ((JavaConstant) store.index().asConstant()).asInt();
return processStore(store, store.array(), arrayLocation, index, store.value(), state, effects);
return processStore(store, store.array(), arrayLocation, index, store.elementKind(), store.value(), state, effects);
} else {
state.killReadCache(arrayLocation, -1);
}
@ -242,13 +245,13 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
if (load.index().isConstant()) {
int index = ((JavaConstant) load.index().asConstant()).asInt();
LocationIdentity arrayLocation = NamedLocationIdentity.getArrayLocation(load.elementKind());
return processLoad(load, load.array(), arrayLocation, index, state, effects);
return processLoad(load, load.array(), arrayLocation, index, load.elementKind(), state, effects);
}
return false;
}
private boolean processUnbox(UnboxNode unbox, PEReadEliminationBlockState state, GraphEffectList effects) {
return processLoad(unbox, unbox.getValue(), UNBOX_LOCATIONS.get(unbox.getBoxingKind()), -1, state, effects);
return processLoad(unbox, unbox.getValue(), UNBOX_LOCATIONS.get(unbox.getBoxingKind()), -1, unbox.getBoxingKind(), state, effects);
}
private static void processIdentity(PEReadEliminationBlockState state, LocationIdentity identity) {
@ -290,7 +293,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
if (object != null) {
Pair<ValueNode, Object> pair = firstValueSet.get(object);
while (pair != null) {
initialState.addReadCache(pair.getLeft(), entry.identity, entry.index, initialState.getReadCache().get(entry), this);
initialState.addReadCache(pair.getLeft(), entry.identity, entry.index, entry.kind, initialState.getReadCache().get(entry), this);
pair = (Pair<ValueNode, Object>) pair.getRight();
}
}
@ -307,7 +310,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
MapCursor<ReadCacheEntry, ValueNode> entry = exitState.getReadCache().getEntries();
while (entry.advance()) {
if (initialState.getReadCache().get(entry.getKey()) != entry.getValue()) {
ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity, entry.getKey().index, this);
ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity, entry.getKey().index, entry.getKey().kind, this);
assert value != null : "Got null from read cache, entry's value:" + entry.getValue();
if (!(value instanceof ProxyNode) || ((ProxyNode) value).proxyPoint() != exitNode) {
ProxyNode proxy = new ValueProxyNode(value, exitNode);
@ -366,7 +369,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
PhiNode phiNode = getPhi(key, value.stamp().unrestricted());
mergeEffects.addFloatingNode(phiNode, "mergeReadCache");
for (int i = 0; i < states.size(); i++) {
ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, PEReadEliminationClosure.this);
ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, key.kind, PEReadEliminationClosure.this);
assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps.";
setPhiInput(phiNode, i, v);
}
@ -383,19 +386,19 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
if (phi.getStackKind() == JavaKind.Object) {
for (ReadCacheEntry entry : states.get(0).readCache.getKeys()) {
if (entry.object == getPhiValueAt(phi, 0)) {
mergeReadCachePhi(phi, entry.identity, entry.index, states);
mergeReadCachePhi(phi, entry.identity, entry.index, entry.kind, states);
}
}
}
}
}
private void mergeReadCachePhi(PhiNode phi, LocationIdentity identity, int index, List<PEReadEliminationBlockState> states) {
private void mergeReadCachePhi(PhiNode phi, LocationIdentity identity, int index, JavaKind kind, List<PEReadEliminationBlockState> states) {
ValueNode[] values = new ValueNode[states.size()];
values[0] = states.get(0).getReadCache(getPhiValueAt(phi, 0), identity, index, PEReadEliminationClosure.this);
values[0] = states.get(0).getReadCache(getPhiValueAt(phi, 0), identity, index, kind, PEReadEliminationClosure.this);
if (values[0] != null) {
for (int i = 1; i < states.size(); i++) {
ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, PEReadEliminationClosure.this);
ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, kind, PEReadEliminationClosure.this);
// e.g. unsafe loads / stores with same identity and different access kinds see
// mergeReadCache(states)
if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) {
@ -404,12 +407,12 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
values[i] = value;
}
PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index), values[0].stamp().unrestricted());
PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index, kind), values[0].stamp().unrestricted());
mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi");
for (int i = 0; i < values.length; i++) {
setPhiInput(phiNode, i, values[i]);
}
newState.readCache.put(new ReadCacheEntry(identity, phi, index), phiNode);
newState.readCache.put(new ReadCacheEntry(identity, phi, index, kind), phiNode);
}
}
}

View File

@ -24,9 +24,7 @@ package org.graalvm.compiler.virtual.phases.ea;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.graph.Node;
@ -54,6 +52,19 @@ public abstract class PartialEscapeBlockState<T extends PartialEscapeBlockState<
*/
private ObjectState[] objectStates;
public boolean contains(VirtualObjectNode value) {
for (ObjectState state : objectStates) {
if (state != null && state.isVirtual() && state.getEntries() != null) {
for (ValueNode entry : state.getEntries()) {
if (entry == value) {
return true;
}
}
}
}
return false;
}
private static class RefCount {
private int refCount = 1;
}
@ -310,41 +321,6 @@ public abstract class PartialEscapeBlockState<T extends PartialEscapeBlockState<
return true;
}
protected static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
if (left.size() != right.size()) {
return false;
}
return compareMapsNoSize(left, right);
}
protected static <K, V> boolean compareMapsNoSize(Map<K, V> left, Map<K, V> right) {
if (left == right) {
return true;
}
for (Map.Entry<K, V> entry : right.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
assert value != null;
V otherValue = left.get(key);
if (otherValue != value && !value.equals(otherValue)) {
return false;
}
}
return true;
}
protected static <U, V> void meetMaps(Map<U, V> target, Map<U, V> source) {
Iterator<Map.Entry<U, V>> iter = target.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<U, V> entry = iter.next();
if (source.containsKey(entry.getKey())) {
assert source.get(entry.getKey()) == entry.getValue();
} else {
iter.remove();
}
}
}
public void resetObjectStates(int size) {
objectStates = new ObjectState[size];
}

View File

@ -1009,12 +1009,7 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
for (int i = 0; i < states.length; i++) {
VirtualObjectNode virtual = virtualObjs[i];
boolean identitySurvives = virtual.hasIdentity() &&
// check whether we trivially see that this is the only
// reference to this allocation
!isSingleUsageAllocation(getPhiValueAt(phi, i));
if (identitySurvives || !firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) {
if (!firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) {
compatible = false;
break;
}
@ -1023,6 +1018,18 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
break;
}
}
if (compatible) {
for (int i = 0; i < states.length; i++) {
VirtualObjectNode virtual = virtualObjs[i];
/*
* check whether we trivially see that this is the only reference to
* this allocation
*/
if (virtual.hasIdentity() && !isSingleUsageAllocation(getPhiValueAt(phi, i), virtualObjs, states[i])) {
compatible = false;
}
}
}
if (compatible) {
VirtualObjectNode virtual = getValueObjectVirtual(phi, virtualObjs[0]);
mergeEffects.addFloatingNode(virtual, "valueObjectNode");
@ -1069,14 +1076,34 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
return materialized;
}
private boolean isSingleUsageAllocation(ValueNode value) {
private boolean isSingleUsageAllocation(ValueNode value, VirtualObjectNode[] virtualObjs, PartialEscapeBlockState<?> state) {
/*
* If the phi input is an allocation, we know that it is a "fresh" value, i.e., that
* this is a value that will only appear through this source, and cannot appear anywhere
* else. If the phi is also the only usage of this input, we know that no other place
* can check object identity against it, so it is safe to lose the object identity here.
*/
return value instanceof AllocatedObjectNode && value.hasExactlyOneUsage();
if (!(value instanceof AllocatedObjectNode && value.hasExactlyOneUsage())) {
return false;
}
/*
* Check that the state only references the one virtual object from the Phi.
*/
VirtualObjectNode singleVirtual = null;
for (int v = 0; v < virtualObjs.length; v++) {
if (state.contains(virtualObjs[v])) {
if (singleVirtual == null) {
singleVirtual = virtualObjs[v];
} else if (singleVirtual != virtualObjs[v]) {
/*
* More than one virtual object is visible in the object state.
*/
return false;
}
}
}
return true;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2011, 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.graphio;
import java.util.Collection;
import java.util.Collections;
final class DefaultGraphBlocks implements GraphBlocks<Object, Object, Object> {
private static final DefaultGraphBlocks DEFAULT = new DefaultGraphBlocks();
private DefaultGraphBlocks() {
}
@SuppressWarnings("unchecked")
public static <G, B, N> GraphBlocks<G, B, N> empty() {
return (GraphBlocks<G, B, N>) DEFAULT;
}
@Override
public Collection<? extends Void> blocks(Object graph) {
return Collections.emptyList();
}
@Override
public int blockId(Object block) {
return -1;
}
@Override
public Collection<? extends Object> blockNodes(Object info, Object block) {
return Collections.emptyList();
}
@Override
public Collection<? extends Object> blockSuccessors(Object block) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2011, 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.graphio;
final class DefaultGraphTypes implements GraphTypes {
static final GraphTypes DEFAULT = new DefaultGraphTypes();
private DefaultGraphTypes() {
}
@Override
public Class<?> enumClass(Object enumValue) {
if (enumValue instanceof Enum<?>) {
return enumValue.getClass();
}
return null;
}
@Override
public int enumOrdinal(Object obj) {
if (obj instanceof Enum<?>) {
return ((Enum<?>) obj).ordinal();
}
return -1;
}
@SuppressWarnings("unchecked")
@Override
public String[] enumTypeValues(Object clazz) {
if (clazz instanceof Class<?>) {
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) clazz;
Enum<?>[] constants = enumClass.getEnumConstants();
if (constants != null) {
String[] names = new String[constants.length];
for (int i = 0; i < constants.length; i++) {
names[i] = constants[i].name();
}
return names;
}
}
return null;
}
@Override
public String typeName(Object clazz) {
if (clazz instanceof Class<?>) {
return ((Class<?>) clazz).getName();
}
return null;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2011, 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.graphio;
import java.util.Collection;
/**
* Special support for dealing with blocks.
*
* @param <G> the type that represents the graph
* @param <B> the type that represents the block
* @param <N> the type of the node
*/
public interface GraphBlocks<G, B, N> {
/**
* All blocks in the graph.
*
* @param graph the graph
* @return collection of blocks in the graph
*/
Collection<? extends B> blocks(G graph);
/**
* Unique id of a block.
*
* @param block the block
* @return the id of the block
*/
int blockId(B block);
Collection<? extends N> blockNodes(G info, B block);
Collection<? extends B> blockSuccessors(B block);
}

View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2011, 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.graphio;
/**
* Representation of methods, fields, their signatures and code locations.
*
* @param <M> type representing methods
* @param <F> type representing fields
* @param <S> type representing signature
* @param <P> type representing source code location
*/
public interface GraphElements<M, F, S, P> {
/**
* Recognize method. Can the object be seen as a method?
*
* @param obj the object to check
* @return <code>null</code> if the object isn't a method, non-null value otherwise
*/
M method(Object obj);
/**
* Bytecode for a method.
*
* @param method the method
* @return bytecode of the method
*/
byte[] methodCode(M method);
/**
* Method modifiers.
*
* @param method the method
* @return its modifiers
*/
int methodModifiers(M method);
/**
* Method's signature.
*
* @param method the method
* @return signature of the method
*/
S methodSignature(M method);
/**
* Method name.
*
* @param method the method
* @return name of the method
*/
String methodName(M method);
/**
* Method's declaring class. The returned object shall be a {@link Class} or be recognizable by
* {@link GraphTypes#typeName(java.lang.Object)} method.
*
* @param method the method
* @return object representing class that defined the method
*/
Object methodDeclaringClass(M method);
/**
* Recognizes a field. Can the object be seen as a field?
*
* @param object the object to check
* @return <code>null</code> if the object isn't a field, non-null value otherwise
*/
F field(Object object);
/**
* Field modifiers.
*
* @param field the field
* @return field modifiers
*/
int fieldModifiers(F field);
/**
* Type name of the field.
*
* @param field the field
* @return the name of the field's type
*/
String fieldTypeName(F field);
/**
* Name of a field.
*
* @param field the field
* @return the name of the field
*/
String fieldName(F field);
/**
* Field's declaring class. The returned object shall be a {@link Class} or be recognizable by
* {@link GraphTypes#typeName(java.lang.Object)} method.
*
* @param field the field
* @return object representing class that defined the field
*/
Object fieldDeclaringClass(F field);
/**
* Recognizes signature. Can the object be seen as a signature?
*
* @param object the object to check
* @return <code>null</code> if the object isn't a signature, non-null value otherwise
*/
S signature(Object object);
/**
* Number of parameters of a signature.
*
* @param signature the signature
* @return number of parameters
*/
int signatureParameterCount(S signature);
/**
* Type name of a signature parameter.
*
* @param signature the signature
* @param index index from 0 to {@link #signatureParameterCount(java.lang.Object)} - 1
* @return the type name
*/
String signatureParameterTypeName(S signature, int index);
/**
* Type name of a return type.
*
* @param signature the signature
* @return the type name
*/
String signatureReturnTypeName(S signature);
/**
* Recognize a source position. Can the object be seen as a position?
*
* @param object the object to check
* @return <code>null</code> if the object isn't a position, non-null otherwise
*/
P nodeSourcePosition(Object object);
/**
* Method for a position.
*
* @param pos the position
* @return the method at the position
*/
M nodeSourcePositionMethod(P pos);
/**
* Caller of a position.
*
* @param pos the position
* @return <code>null</code> or another position
*/
P nodeSourcePositionCaller(P pos);
/**
* Byte code index of a position.
*
* @param pos the position
* @return the BCI of the position
*/
int nodeSourcePositionBCI(P pos);
/**
* Stack trace element for a method, index and position.
*
* @param method the method
* @param bci the index
* @param pos the position
* @return stack trace element for the method, index and position
*/
StackTraceElement methodStackTraceElement(M method, int bci, P pos);
}

View File

@ -0,0 +1,168 @@
/*
* Copyright (c) 2011, 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.graphio;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
import java.util.Map;
/**
* Instance of output to dump informations about a compiler compilations.
*
* @param <G> the type of graph this instance handles
* @param <M> the type of methods this instance handles
*/
public final class GraphOutput<G, M> implements Closeable {
private final GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?> printer;
private GraphOutput(GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?> p) {
this.printer = p;
}
/**
* Creates new builder to configure a future instance of {@link GraphOutput}.
*
* @param <G> the type of the graph
* @param <N> the type of the nodes
* @param <C> the type of the node classes
* @param <P> the type of the ports
*
* @param structure description of the structure of the graph
* @return the builder to configure
*/
public static <G, N, C, P> Builder<G, N, ?> newBuilder(GraphStructure<G, N, C, P> structure) {
return new Builder<>(structure);
}
/**
* Begins a compilation group.
*
* @param forGraph
* @param name
* @param shortName
* @param method
* @param bci
* @param properties
* @throws IOException
*/
public void beginGroup(G forGraph, String name, String shortName, M method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException {
printer.beginGroup(forGraph, name, shortName, method, bci, properties);
}
/**
* Prints a single graph.
*
* @param graph
* @param properties
* @param id
* @param format
* @param args
* @throws IOException
*/
public void print(G graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException {
printer.print(graph, properties, id, format, args);
}
/**
* Ends compilation group.
*
* @throws IOException
*/
public void endGroup() throws IOException {
printer.endGroup();
}
/**
* Closes the output. Closes allocated resources and associated output channel.
*/
@Override
public void close() {
printer.close();
}
/**
* Builder to configure and create an instance of {@link GraphOutput}.
*
* @param <G> the type of the (root element of) graph
* @param <N> the type of the nodes
* @param <M> the type of the methods
*/
public static final class Builder<G, N, M> {
private final GraphStructure<G, N, ?, ?> structure;
private GraphElements<M, ?, ?, ?> elements = null;
private GraphTypes types = DefaultGraphTypes.DEFAULT;
private GraphBlocks<G, ?, N> blocks = DefaultGraphBlocks.empty();
Builder(GraphStructure<G, N, ?, ?> structure) {
this.structure = structure;
}
/**
* Associates different implementation of types.
*
* @param graphTypes implementation of types and enum recognition
* @return this builder
*/
public Builder<G, N, M> types(GraphTypes graphTypes) {
this.types = graphTypes;
return this;
}
/**
* Associates implementation of blocks.
*
* @param graphBlocks the blocks implementation
* @return this builder
*/
public Builder<G, N, M> blocks(GraphBlocks<G, ?, N> graphBlocks) {
this.blocks = graphBlocks;
return this;
}
/**
* Associates implementation of graph elements.
*
* @param graphElements the elements implementation
* @return this builder
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public <E> Builder<G, N, E> elements(GraphElements<E, ?, ?, ?> graphElements) {
this.elements = (GraphElements) graphElements;
return (Builder<G, N, E>) this;
}
/**
* Creates new {@link GraphOutput} to output to provided channel. The output will use
* interfaces currently associated with this builder.
*
* @param channel the channel to output to
* @return new graph output
* @throws IOException if something goes wrong when writing to the channel
*/
public GraphOutput<G, M> build(WritableByteChannel channel) throws IOException {
ProtocolImpl<G, N, ?, ?, ?, M, ?, ?, ?> p = new ProtocolImpl<>(structure, types, blocks, elements, channel);
return new GraphOutput<>(p);
}
}
}

View File

@ -0,0 +1,678 @@
/*
* Copyright (c) 2011, 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.graphio;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
abstract class GraphProtocol<Graph, Node, NodeClass, Edges, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> implements Closeable {
private static final Charset UTF8 = Charset.forName("UTF-8");
private static final int CONSTANT_POOL_MAX_SIZE = 8000;
private static final int BEGIN_GROUP = 0x00;
private static final int BEGIN_GRAPH = 0x01;
private static final int CLOSE_GROUP = 0x02;
private static final int POOL_NEW = 0x00;
private static final int POOL_STRING = 0x01;
private static final int POOL_ENUM = 0x02;
private static final int POOL_CLASS = 0x03;
private static final int POOL_METHOD = 0x04;
private static final int POOL_NULL = 0x05;
private static final int POOL_NODE_CLASS = 0x06;
private static final int POOL_FIELD = 0x07;
private static final int POOL_SIGNATURE = 0x08;
private static final int POOL_NODE_SOURCE_POSITION = 0x09;
private static final int PROPERTY_POOL = 0x00;
private static final int PROPERTY_INT = 0x01;
private static final int PROPERTY_LONG = 0x02;
private static final int PROPERTY_DOUBLE = 0x03;
private static final int PROPERTY_FLOAT = 0x04;
private static final int PROPERTY_TRUE = 0x05;
private static final int PROPERTY_FALSE = 0x06;
private static final int PROPERTY_ARRAY = 0x07;
private static final int PROPERTY_SUBGRAPH = 0x08;
private static final int KLASS = 0x00;
private static final int ENUM_KLASS = 0x01;
private static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'};
private final ConstantPool constantPool;
private final ByteBuffer buffer;
private final WritableByteChannel channel;
private final int versionMajor;
private final int versionMinor;
protected GraphProtocol(WritableByteChannel channel) throws IOException {
this(channel, 4, 0);
}
private GraphProtocol(WritableByteChannel channel, int major, int minor) throws IOException {
if (major > 4) {
throw new IllegalArgumentException();
}
if (major == 4 && minor > 0) {
throw new IllegalArgumentException();
}
this.versionMajor = major;
this.versionMinor = minor;
this.constantPool = new ConstantPool();
this.buffer = ByteBuffer.allocateDirect(256 * 1024);
this.channel = channel;
writeVersion();
}
@SuppressWarnings("all")
public final void print(Graph graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException {
writeByte(BEGIN_GRAPH);
if (versionMajor >= 3) {
writeInt(id);
writeString(format);
writeInt(args.length);
for (Object a : args) {
writePropertyObject(graph, a);
}
} else {
writePoolObject(formatTitle(graph, id, format, args));
}
writeGraph(graph, properties);
flush();
}
public final void beginGroup(Graph noGraph, String name, String shortName, ResolvedJavaMethod method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException {
writeByte(BEGIN_GROUP);
writePoolObject(name);
writePoolObject(shortName);
writePoolObject(method);
writeInt(bci);
writeProperties(noGraph, properties);
}
public final void endGroup() throws IOException {
writeByte(CLOSE_GROUP);
}
@Override
public final void close() {
try {
flush();
channel.close();
} catch (IOException ex) {
throw new Error(ex);
}
}
protected abstract Graph findGraph(Graph current, Object obj);
protected abstract ResolvedJavaMethod findMethod(Object obj);
protected abstract NodeClass findNodeClass(Object obj);
/**
* Find a Java class. The returned object must be acceptable by
* {@link #findJavaTypeName(java.lang.Object)} and return valid name for the class.
*
* @param clazz node class object
* @return object representing the class, for example {@link Class}
*/
protected abstract Object findJavaClass(NodeClass clazz);
protected abstract Object findEnumClass(Object enumValue);
protected abstract String findNameTemplate(NodeClass clazz);
protected abstract Edges findClassEdges(NodeClass nodeClass, boolean dumpInputs);
protected abstract int findNodeId(Node n);
protected abstract void findExtraNodes(Node node, Collection<? super Node> extraNodes);
protected abstract boolean hasPredecessor(Node node);
protected abstract int findNodesCount(Graph info);
protected abstract Iterable<? extends Node> findNodes(Graph info);
protected abstract void findNodeProperties(Node node, Map<String, Object> props, Graph info);
protected abstract Collection<? extends Node> findBlockNodes(Graph info, Block block);
protected abstract int findBlockId(Block sux);
protected abstract Collection<? extends Block> findBlocks(Graph graph);
protected abstract Collection<? extends Block> findBlockSuccessors(Block block);
protected abstract String formatTitle(Graph graph, int id, String format, Object... args);
protected abstract int findSize(Edges edges);
protected abstract boolean isDirect(Edges edges, int i);
protected abstract String findName(Edges edges, int i);
protected abstract Object findType(Edges edges, int i);
protected abstract Collection<? extends Node> findNodes(Graph graph, Node node, Edges edges, int i);
protected abstract int findEnumOrdinal(Object obj);
protected abstract String[] findEnumTypeValues(Object clazz);
protected abstract String findJavaTypeName(Object obj);
protected abstract byte[] findMethodCode(ResolvedJavaMethod method);
protected abstract int findMethodModifiers(ResolvedJavaMethod method);
protected abstract Signature findMethodSignature(ResolvedJavaMethod method);
protected abstract String findMethodName(ResolvedJavaMethod method);
protected abstract Object findMethodDeclaringClass(ResolvedJavaMethod method);
protected abstract int findFieldModifiers(ResolvedJavaField field);
protected abstract String findFieldTypeName(ResolvedJavaField field);
protected abstract String findFieldName(ResolvedJavaField field);
protected abstract Object findFieldDeclaringClass(ResolvedJavaField field);
protected abstract ResolvedJavaField findJavaField(Object object);
protected abstract Signature findSignature(Object object);
protected abstract int findSignatureParameterCount(Signature signature);
protected abstract String findSignatureParameterTypeName(Signature signature, int index);
protected abstract String findSignatureReturnTypeName(Signature signature);
protected abstract NodeSourcePosition findNodeSourcePosition(Object object);
protected abstract ResolvedJavaMethod findNodeSourcePositionMethod(NodeSourcePosition pos);
protected abstract NodeSourcePosition findNodeSourcePositionCaller(NodeSourcePosition pos);
protected abstract int findNodeSourcePositionBCI(NodeSourcePosition pos);
protected abstract StackTraceElement findMethodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos);
private void writeVersion() throws IOException {
writeBytesRaw(MAGIC_BYTES);
writeByte(versionMajor);
writeByte(versionMinor);
}
private void flush() throws IOException {
buffer.flip();
/*
* Try not to let interrupted threads aborting the write. There's still a race here but an
* interrupt that's been pending for a long time shouldn't stop this writing.
*/
boolean interrupted = Thread.interrupted();
try {
channel.write(buffer);
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
buffer.compact();
}
private void ensureAvailable(int i) throws IOException {
assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small";
while (buffer.remaining() < i) {
flush();
}
}
private void writeByte(int b) throws IOException {
ensureAvailable(1);
buffer.put((byte) b);
}
private void writeInt(int b) throws IOException {
ensureAvailable(4);
buffer.putInt(b);
}
private void writeLong(long b) throws IOException {
ensureAvailable(8);
buffer.putLong(b);
}
private void writeDouble(double b) throws IOException {
ensureAvailable(8);
buffer.putDouble(b);
}
private void writeFloat(float b) throws IOException {
ensureAvailable(4);
buffer.putFloat(b);
}
private void writeShort(char b) throws IOException {
ensureAvailable(2);
buffer.putChar(b);
}
private void writeString(String str) throws IOException {
byte[] bytes = str.getBytes(UTF8);
writeBytes(bytes);
}
private void writeBytes(byte[] b) throws IOException {
if (b == null) {
writeInt(-1);
} else {
writeInt(b.length);
writeBytesRaw(b);
}
}
private void writeBytesRaw(byte[] b) throws IOException {
int bytesWritten = 0;
while (bytesWritten < b.length) {
int toWrite = Math.min(b.length - bytesWritten, buffer.capacity());
ensureAvailable(toWrite);
buffer.put(b, bytesWritten, toWrite);
bytesWritten += toWrite;
}
}
private void writeInts(int[] b) throws IOException {
if (b == null) {
writeInt(-1);
} else {
writeInt(b.length);
int sizeInBytes = b.length * 4;
ensureAvailable(sizeInBytes);
buffer.asIntBuffer().put(b);
buffer.position(buffer.position() + sizeInBytes);
}
}
private void writeDoubles(double[] b) throws IOException {
if (b == null) {
writeInt(-1);
} else {
writeInt(b.length);
int sizeInBytes = b.length * 8;
ensureAvailable(sizeInBytes);
buffer.asDoubleBuffer().put(b);
buffer.position(buffer.position() + sizeInBytes);
}
}
private void writePoolObject(Object object) throws IOException {
if (object == null) {
writeByte(POOL_NULL);
return;
}
Character id = constantPool.get(object);
if (id == null) {
addPoolEntry(object);
} else {
if (object instanceof Enum<?> || findEnumOrdinal(object) >= 0) {
writeByte(POOL_ENUM);
} else if (object instanceof Class<?> || findJavaTypeName(object) != null) {
writeByte(POOL_CLASS);
} else if (findJavaField(object) != null) {
writeByte(POOL_FIELD);
} else if (findSignature(object) != null) {
writeByte(POOL_SIGNATURE);
} else if (versionMajor >= 4 && findNodeSourcePosition(object) != null) {
writeByte(POOL_NODE_SOURCE_POSITION);
} else {
if (findNodeClass(object) != null) {
writeByte(POOL_NODE_CLASS);
} else if (findMethod(object) != null) {
writeByte(POOL_METHOD);
} else {
writeByte(POOL_STRING);
}
}
writeShort(id.charValue());
}
}
private void writeGraph(Graph graph, Map<? extends Object, ? extends Object> properties) throws IOException {
writeProperties(graph, properties);
writeNodes(graph);
writeBlocks(findBlocks(graph), graph);
}
private void writeNodes(Graph info) throws IOException {
Map<String, Object> props = new HashMap<>();
final int size = findNodesCount(info);
writeInt(size);
int cnt = 0;
for (Node node : findNodes(info)) {
NodeClass nodeClass = findNodeClass(node);
if (nodeClass == null) {
throw new IOException("No class for " + node);
}
findNodeProperties(node, props, info);
writeInt(findNodeId(node));
writePoolObject(nodeClass);
writeByte(hasPredecessor(node) ? 1 : 0);
writeProperties(info, props);
writeEdges(info, node, true);
writeEdges(info, node, false);
props.clear();
cnt++;
}
if (size != cnt) {
throw new IOException("Expecting " + size + " nodes, but found " + cnt);
}
}
private void writeEdges(Graph graph, Node node, boolean dumpInputs) throws IOException {
NodeClass clazz = findNodeClass(node);
Edges edges = findClassEdges(clazz, dumpInputs);
int size = findSize(edges);
for (int i = 0; i < size; i++) {
Collection<? extends Node> list = findNodes(graph, node, edges, i);
if (isDirect(edges, i)) {
if (list != null && list.size() != 1) {
throw new IOException("Edge " + i + " in " + edges + " is direct, but list isn't singleton: " + list);
}
Node n = null;
if (list != null && !list.isEmpty()) {
n = list.iterator().next();
}
writeNodeRef(n);
} else {
if (list == null) {
writeShort((char) 0);
} else {
int listSize = list.size();
assert listSize == ((char) listSize);
writeShort((char) listSize);
for (Node edge : list) {
writeNodeRef(edge);
}
}
}
}
}
private void writeNodeRef(Node node) throws IOException {
writeInt(findNodeId(node));
}
private void writeBlocks(Collection<? extends Block> blocks, Graph info) throws IOException {
if (blocks != null) {
for (Block block : blocks) {
Collection<? extends Node> nodes = findBlockNodes(info, block);
if (nodes == null) {
writeInt(0);
return;
}
}
writeInt(blocks.size());
for (Block block : blocks) {
Collection<? extends Node> nodes = findBlockNodes(info, block);
writeInt(findBlockId(block));
writeInt(nodes.size());
for (Node node : nodes) {
writeInt(findNodeId(node));
}
final Collection<? extends Block> successors = findBlockSuccessors(block);
writeInt(successors.size());
for (Block sux : successors) {
writeInt(findBlockId(sux));
}
}
} else {
writeInt(0);
}
}
private void writeEdgesInfo(NodeClass nodeClass, boolean dumpInputs) throws IOException {
Edges edges = findClassEdges(nodeClass, dumpInputs);
int size = findSize(edges);
writeShort((char) size);
for (int i = 0; i < size; i++) {
writeByte(isDirect(edges, i) ? 0 : 1);
writePoolObject(findName(edges, i));
if (dumpInputs) {
writePoolObject(findType(edges, i));
}
}
}
@SuppressWarnings("all")
private void addPoolEntry(Object object) throws IOException {
ResolvedJavaField field;
String typeName;
Signature signature;
NodeSourcePosition pos;
int enumOrdinal;
char index = constantPool.add(object);
writeByte(POOL_NEW);
writeShort(index);
if ((typeName = findJavaTypeName(object)) != null) {
writeByte(POOL_CLASS);
writeString(typeName);
String[] enumValueNames = findEnumTypeValues(object);
if (enumValueNames != null) {
writeByte(ENUM_KLASS);
writeInt(enumValueNames.length);
for (String o : enumValueNames) {
writePoolObject(o);
}
} else {
writeByte(KLASS);
}
} else if ((enumOrdinal = findEnumOrdinal(object)) >= 0) {
writeByte(POOL_ENUM);
writePoolObject(findEnumClass(object));
writeInt(enumOrdinal);
} else if ((field = findJavaField(object)) != null) {
writeByte(POOL_FIELD);
writePoolObject(findFieldDeclaringClass(field));
writePoolObject(findFieldName(field));
writePoolObject(findFieldTypeName(field));
writeInt(findFieldModifiers(field));
} else if ((signature = findSignature(object)) != null) {
writeByte(POOL_SIGNATURE);
int args = findSignatureParameterCount(signature);
writeShort((char) args);
for (int i = 0; i < args; i++) {
writePoolObject(findSignatureParameterTypeName(signature, i));
}
writePoolObject(findSignatureReturnTypeName(signature));
} else if (versionMajor >= 4 && (pos = findNodeSourcePosition(object)) != null) {
writeByte(POOL_NODE_SOURCE_POSITION);
ResolvedJavaMethod method = findNodeSourcePositionMethod(pos);
writePoolObject(method);
final int bci = findNodeSourcePositionBCI(pos);
writeInt(bci);
StackTraceElement ste = findMethodStackTraceElement(method, bci, pos);
if (ste != null) {
writePoolObject(ste.getFileName());
writeInt(ste.getLineNumber());
} else {
writePoolObject(null);
}
writePoolObject(findNodeSourcePositionCaller(pos));
} else {
NodeClass nodeClass = findNodeClass(object);
if (nodeClass != null) {
writeByte(POOL_NODE_CLASS);
final Object clazz = findJavaClass(nodeClass);
if (versionMajor >= 3) {
writePoolObject(clazz);
writeString(findNameTemplate(nodeClass));
} else {
writeString(((Class<?>) clazz).getSimpleName());
String nameTemplate = findNameTemplate(nodeClass);
writeString(nameTemplate);
}
writeEdgesInfo(nodeClass, true);
writeEdgesInfo(nodeClass, false);
return;
}
ResolvedJavaMethod method = findMethod(object);
if (method == null) {
writeByte(POOL_STRING);
writeString(object.toString());
return;
}
writeByte(POOL_METHOD);
writePoolObject(findMethodDeclaringClass(method));
writePoolObject(findMethodName(method));
writePoolObject(findMethodSignature(method));
writeInt(findMethodModifiers(method));
writeBytes(findMethodCode(method));
}
}
private void writePropertyObject(Graph graph, Object obj) throws IOException {
if (obj instanceof Integer) {
writeByte(PROPERTY_INT);
writeInt(((Integer) obj).intValue());
} else if (obj instanceof Long) {
writeByte(PROPERTY_LONG);
writeLong(((Long) obj).longValue());
} else if (obj instanceof Double) {
writeByte(PROPERTY_DOUBLE);
writeDouble(((Double) obj).doubleValue());
} else if (obj instanceof Float) {
writeByte(PROPERTY_FLOAT);
writeFloat(((Float) obj).floatValue());
} else if (obj instanceof Boolean) {
if (((Boolean) obj).booleanValue()) {
writeByte(PROPERTY_TRUE);
} else {
writeByte(PROPERTY_FALSE);
}
} else if (obj != null && obj.getClass().isArray()) {
Class<?> componentType = obj.getClass().getComponentType();
if (componentType.isPrimitive()) {
if (componentType == Double.TYPE) {
writeByte(PROPERTY_ARRAY);
writeByte(PROPERTY_DOUBLE);
writeDoubles((double[]) obj);
} else if (componentType == Integer.TYPE) {
writeByte(PROPERTY_ARRAY);
writeByte(PROPERTY_INT);
writeInts((int[]) obj);
} else {
writeByte(PROPERTY_POOL);
writePoolObject(obj);
}
} else {
writeByte(PROPERTY_ARRAY);
writeByte(PROPERTY_POOL);
Object[] array = (Object[]) obj;
writeInt(array.length);
for (Object o : array) {
writePoolObject(o);
}
}
} else {
Graph g = findGraph(graph, obj);
if (g == null) {
writeByte(PROPERTY_POOL);
writePoolObject(obj);
} else {
writeByte(PROPERTY_SUBGRAPH);
writeGraph(g, null);
}
}
}
private void writeProperties(Graph graph, Map<? extends Object, ? extends Object> props) throws IOException {
if (props == null) {
writeShort((char) 0);
return;
}
final int size = props.size();
// properties
writeShort((char) size);
int cnt = 0;
for (Map.Entry<? extends Object, ? extends Object> entry : props.entrySet()) {
String key = entry.getKey().toString();
writePoolObject(key);
writePropertyObject(graph, entry.getValue());
cnt++;
}
if (size != cnt) {
throw new IOException("Expecting " + size + " properties, but found only " + cnt);
}
}
private static final class ConstantPool extends LinkedHashMap<Object, Character> {
private final LinkedList<Character> availableIds;
private char nextId;
private static final long serialVersionUID = -2676889957907285681L;
ConstantPool() {
super(50, 0.65f);
availableIds = new LinkedList<>();
}
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) {
if (size() > CONSTANT_POOL_MAX_SIZE) {
availableIds.addFirst(eldest.getValue());
return true;
}
return false;
}
private Character nextAvailableId() {
if (!availableIds.isEmpty()) {
return availableIds.removeFirst();
}
return nextId++;
}
public char add(Object obj) {
Character id = nextAvailableId();
put(obj, id);
return id;
}
}
}

View File

@ -0,0 +1,206 @@
/*
* Copyright (c) 2011, 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.graphio;
import java.util.Collection;
import java.util.Map;
/**
* Interface that defines structure of a compiler graph. The structure of a graph is composed from
* nodes with properties, the classes of individual nodes, and ports associated with each node that
* may contain edges to other nodes. The structure of a graph is assumed to be immutable for the
* time of {@link GraphOutput operations} on it.
*
* @param <G> the type of the (root node of a) graph
* @param <N> the type of nodes
* @param <C> the type of node classes
* @param <P> the type of node ports
*/
public interface GraphStructure<G, N, C, P> {
/**
* Casts the provided object to graph, if possible. If the given object <code>obj</code> can be
* seen as a graph or sub-graph of a graph, then return the properly typed instance. Otherwise
* return <code>null</code>
*
* @param currentGraph the currently processed graph
* @param obj an object to check and view as a graph
* @return appropriate graph object or <code>null</code> if the object doesn't represent a graph
*/
G graph(G currentGraph, Object obj);
/**
* Nodes of a graph. Each graph is composed from a fixed set of nodes. This method returns an
* iterable which provides access to all of them - the number of nodes provided by the iterable
* must match the number returned by {@link #nodesCount(java.lang.Object)} method.
*
* @see #nodesCount(java.lang.Object)
* @param graph the graph to query for nodes
* @return iterable with all the graph's nodes
*/
Iterable<? extends N> nodes(G graph);
/**
* Number of nodes in a graph. The number must match the content returned by
* {@link #nodes(java.lang.Object)} method.
*
* @param graph the graph to query
* @return the number of nodes that will be returned by {@link #nodes(java.lang.Object)}
*/
int nodesCount(G graph);
/**
* Id of a node. Each node in the graph is uniquely identified by a integer value. If two nodes
* have the same id, then they shall be <code>==</code> to each other.
*
* @param node the node to query for an id
* @return the id of the node
*/
int nodeId(N node);
/**
* Checks if there is a predecessor for a node.
*
* @param node the node to check
* @return <code>true</code> if it has a predecessor, <code>false</code> otherwise
*/
boolean nodeHasPredecessor(N node);
/**
* Collects node properties. Each node can be associated with additional properties identified
* by their name. This method shall copy them into the provided map.
*
* @param graph the current graph
* @param node the node to collect properties for
* @param properties the map to put the properties to
*/
void nodeProperties(G graph, N node, Map<String, ? super Object> properties);
/**
* Finds the node class for the provided object, if possible. If the given object
* <code>obj</code> can be seen as an instance of node class or it is a node in this graph,
* return the properly typed instance of the node class. Otherwise return <code>null</code>
*
* @param obj an object to find node class for
* @return appropriate graph object or <code>null</code> if the object doesn't represent a graph
*/
C nodeClass(Object obj);
/**
* The template used to build the name of nodes of this class. The template may use references
* to inputs (&#123;i#inputName&#125;) and its properties (&#123;p#propertyName&#125;).
*
* @param nodeClass the node class to find name template for
* @return the string representing the template
*/
String nameTemplate(C nodeClass);
/**
* Java class for a node class.
*
* @param nodeClass the node class
* @return the {@link Class} or other type representation of the node class
*/
Object nodeClassType(C nodeClass);
/**
* Input ports of a node class. Each node class has a fixed set of ports where individual edges
* can attach to.
*
* @param nodeClass the node class
* @return input ports for the node class
*/
P portInputs(C nodeClass);
/**
* Output ports of a node class. Each node class has a fixed set of ports from where individual
* edges can point to other nodes.
*
* @param nodeClass the node class
* @return output ports for the node class
*/
P portOutputs(C nodeClass);
/**
* The number of edges in a port. The protocol will then call methods
* {@link #edgeDirect(java.lang.Object, int)}, {@link #edgeName(java.lang.Object, int)},
* {@link #edgeType(java.lang.Object, int)} and
* {@link #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int)} for indexes
* from <code>0</code> to <code>portSize - 1</code>
*
* @param port the port
* @return number of edges in this port
*/
int portSize(P port);
/**
* Checks whether an edge is direct. Direct edge shall have exactly one
* {@linkplain #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int) node} - it
* is an error to return more than one for such an edge from the
* {@linkplain #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int) method}.
*
* @param port the port
* @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
* <code>1</code>
* @return <code>true</code> if only one node can be returned from
* {@link #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int)} method
*/
boolean edgeDirect(P port, int index);
/**
* The name of an edge.
*
* @param port the port
* @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
* <code>1</code>
* @return the name of the edge
*/
String edgeName(P port, int index);
/**
* Type of an edge. The type must be a graph
* <q>enum</q> - e.g. either real instance of {@link Enum} subclass, or something that the
* {@link GraphOutput.Builder} can recognize as
* <q>enum</q>.
*
* @param port
* @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
* <code>1</code>
* @return any {@link Enum} representing type of the edge
*/
Object edgeType(P port, int index);
/**
* Nodes where the edges for a port lead to/from. This method is called for both
* {@link #edgeDirect(java.lang.Object, int) direct/non-direct edges}. In case of a direct edge
* the returned collection must have exactly one element.
*
* @param graph the graph
* @param node the node in the graph
* @param port port of the node class
* @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
* <code>1</code>
* @return <code>null</code> if there are no edges associated with given port or collection of
* nodes where to/from the edges lead to
*/
Collection<? extends N> edgeNodes(G graph, N node, P port, int index);
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2011, 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.graphio;
/**
* Special support for dealing with enums. Normally one can represent various {@link GraphOutput
* graph} enum values with real {@link Enum} instances. In case this is not possible, the
* {@link GraphOutput.Builder} allows one to
* {@link GraphOutput.Builder#types(org.graalvm.graphio.GraphTypes) register} an implementation of
* this interface to treat them specially.
*/
public interface GraphTypes {
/**
* Recognizes an
* <q>enum</q> object. If the <code>enumValue</code> object represents an enum, then an object
* that represents its class shall be returned.
*
* @param enumValue the value to test
* @return <code>null</code> if the value isn't enum, otherwise its class
*/
Object enumClass(Object enumValue);
/**
* Ordinal of an enum. If the <code>obj</code> represents an enum, then return its ordinal
* number otherwise return <code>-1</code>
*
* @param obj the value to test
* @return <code>-1</code> if the obj isn't enum, otherwise its ordinal number
*/
int enumOrdinal(Object obj);
/**
* All possible values of an enum. If the provided <code>maybeEnumClass</code> object represents
* an enum, then compute enum value names in ordinal order and return them as a string array.
* Otherwise return <code>null</code>
*
* @param maybeEnumClass the class to test
* @return <code>null</code> if the clazz isn't an enum, otherwise names of its values
*/
String[] enumTypeValues(Object maybeEnumClass);
/**
* Finds Java type name for a given class.
*
* @param maybeClass object representing the class
* @return the type name of the class or <code>null</code> if the parameter doesn't represent a
* class
*/
String typeName(Object maybeClass);
}

View File

@ -0,0 +1,273 @@
/*
* Copyright (c) 2011, 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.graphio;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
import java.util.Collection;
import java.util.Map;
final class ProtocolImpl<Graph, Node, NodeClass, Port, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition>
extends GraphProtocol<Graph, Node, NodeClass, Port, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> {
private final GraphStructure<Graph, Node, NodeClass, Port> structure;
private final GraphTypes types;
private final GraphBlocks<Graph, Block, Node> blocks;
private final GraphElements<ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> elements;
ProtocolImpl(GraphStructure<Graph, Node, NodeClass, Port> structure, GraphTypes enums, GraphBlocks<Graph, Block, Node> blocks,
GraphElements<ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> elements, WritableByteChannel channel) throws IOException {
super(channel);
this.structure = structure;
this.types = enums;
this.blocks = blocks;
this.elements = elements;
}
@Override
protected Graph findGraph(Graph current, Object obj) {
return structure.graph(current, obj);
}
@Override
protected NodeClass findNodeClass(Object obj) {
return structure.nodeClass(obj);
}
@Override
protected String findNameTemplate(NodeClass clazz) {
return structure.nameTemplate(clazz);
}
@Override
protected int findNodeId(Node n) {
return structure.nodeId(n);
}
@Override
protected boolean hasPredecessor(Node node) {
return structure.nodeHasPredecessor(node);
}
@Override
protected int findNodesCount(Graph info) {
return structure.nodesCount(info);
}
@Override
protected Iterable<? extends Node> findNodes(Graph info) {
return structure.nodes(info);
}
@Override
protected void findNodeProperties(Node node, Map<String, Object> props, Graph info) {
structure.nodeProperties(info, node, props);
}
@Override
protected Port findClassEdges(NodeClass nodeClass, boolean dumpInputs) {
if (dumpInputs) {
return structure.portInputs(nodeClass);
} else {
return structure.portOutputs(nodeClass);
}
}
@Override
protected int findSize(Port edges) {
return structure.portSize(edges);
}
@Override
protected boolean isDirect(Port edges, int i) {
return structure.edgeDirect(edges, i);
}
@Override
protected String findName(Port edges, int i) {
return structure.edgeName(edges, i);
}
@Override
protected Object findType(Port edges, int i) {
return structure.edgeType(edges, i);
}
@Override
protected Collection<? extends Node> findNodes(Graph graph, Node node, Port port, int i) {
return structure.edgeNodes(graph, node, port, i);
}
@Override
protected Object findJavaClass(NodeClass clazz) {
return structure.nodeClassType(clazz);
}
@Override
protected Object findEnumClass(Object enumValue) {
return types.enumClass(enumValue);
}
@Override
protected int findEnumOrdinal(Object obj) {
return types.enumOrdinal(obj);
}
@Override
protected String[] findEnumTypeValues(Object clazz) {
return types.enumTypeValues(clazz);
}
@Override
protected String findJavaTypeName(Object obj) {
return types.typeName(obj);
}
@Override
protected Collection<? extends Node> findBlockNodes(Graph info, Block block) {
return blocks.blockNodes(info, block);
}
@Override
protected int findBlockId(Block block) {
return blocks.blockId(block);
}
@Override
protected Collection<? extends Block> findBlocks(Graph graph) {
return blocks.blocks(graph);
}
@Override
protected Collection<? extends Block> findBlockSuccessors(Block block) {
return blocks.blockSuccessors(block);
}
@Override
protected ResolvedJavaMethod findMethod(Object obj) {
return elements == null ? null : elements.method(obj);
}
@Override
protected byte[] findMethodCode(ResolvedJavaMethod method) {
return elements.methodCode(method);
}
@Override
protected int findMethodModifiers(ResolvedJavaMethod method) {
return elements.methodModifiers(method);
}
@Override
protected Signature findMethodSignature(ResolvedJavaMethod method) {
return elements.methodSignature(method);
}
@Override
protected String findMethodName(ResolvedJavaMethod method) {
return elements.methodName(method);
}
@Override
protected Object findMethodDeclaringClass(ResolvedJavaMethod method) {
return elements.methodDeclaringClass(method);
}
@Override
protected int findFieldModifiers(ResolvedJavaField field) {
return elements.fieldModifiers(field);
}
@Override
protected String findFieldTypeName(ResolvedJavaField field) {
return elements.fieldTypeName(field);
}
@Override
protected String findFieldName(ResolvedJavaField field) {
return elements.fieldName(field);
}
@Override
protected Object findFieldDeclaringClass(ResolvedJavaField field) {
return elements.fieldDeclaringClass(field);
}
@Override
protected ResolvedJavaField findJavaField(Object object) {
return elements == null ? null : elements.field(object);
}
@Override
protected Signature findSignature(Object object) {
return elements == null ? null : elements.signature(object);
}
@Override
protected int findSignatureParameterCount(Signature signature) {
return elements.signatureParameterCount(signature);
}
@Override
protected String findSignatureParameterTypeName(Signature signature, int index) {
return elements.signatureParameterTypeName(signature, index);
}
@Override
protected String findSignatureReturnTypeName(Signature signature) {
return elements.signatureReturnTypeName(signature);
}
@Override
protected NodeSourcePosition findNodeSourcePosition(Object object) {
return elements == null ? null : elements.nodeSourcePosition(object);
}
@Override
protected ResolvedJavaMethod findNodeSourcePositionMethod(NodeSourcePosition pos) {
return elements.nodeSourcePositionMethod(pos);
}
@Override
protected NodeSourcePosition findNodeSourcePositionCaller(NodeSourcePosition pos) {
return elements.nodeSourcePositionCaller(pos);
}
@Override
protected int findNodeSourcePositionBCI(NodeSourcePosition pos) {
return elements.nodeSourcePositionBCI(pos);
}
@Override
protected StackTraceElement findMethodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
return elements.methodStackTraceElement(method, bci, pos);
}
@Override
protected void findExtraNodes(Node node, Collection<? super Node> extraNodes) {
}
@Override
protected String formatTitle(Graph graph, int id, String format, Object... args) {
return String.format(format, args) + " [" + id + "]";
}
}

View File

@ -151,15 +151,17 @@ public final class OptionDescriptor {
public static <T> Builder newBuilder(OptionKey<T> key, String name) {
Objects.requireNonNull(key);
Objects.requireNonNull(name);
return new Builder(key, name);
return EMPTY.new Builder(key, name);
}
private static final OptionDescriptor EMPTY = new OptionDescriptor(null, null, null, null, false);
/**
* Represents an option descriptor builder.
*
* @since 1.0
*/
public static final class Builder {
public final class Builder {
private final OptionKey<?> key;
private final String name;