# HG changeset patch # User dholmes # Date 1496283678 0 # Node ID 4a5b7a2dd7833bec679ebb5ce1804a35cd69cb8c # Parent b32d3928ad6a8a2530efa19490555a251341a410# Parent 8cc204b35e392201cdf7cd305419a254d790003d Merge diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCArithmeticLIRGenerator.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCArithmeticLIRGenerator.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCArithmeticLIRGenerator.java Thu Jun 01 02:21:18 2017 +0000 @@ -113,12 +113,7 @@ @Override public Variable emitBitCount(Value operand) { Variable result = getLIRGen().newVariable(LIRKind.combine(operand).changeType(SPARCKind.WORD)); - AllocatableValue usedOperand = getLIRGen().asAllocatable(operand); - if (operand.getPlatformKind() == SPARCKind.WORD) { // Zero extend - AllocatableValue intermediateOperand = getLIRGen().newVariable(operand.getValueKind()); - getLIRGen().append(new SPARCOP3Op(Op3s.Srl, usedOperand, g0.asValue(), intermediateOperand)); - usedOperand = intermediateOperand; - } + AllocatableValue usedOperand = getLIRGen().asAllocatable(emitZeroExtend(operand)); getLIRGen().append(new SPARCOP3Op(Op3s.Popc, g0.asValue(), usedOperand, result)); return result; } @@ -275,7 +270,7 @@ } return result; } else { - return emitBinary(resultKind, setFlags ? Op3s.Mulscc : Op3s.Mulx, a, b); + return emitBinary(resultKind, Op3s.Mulx, a, b); } } else { boolean isDouble = a.getPlatformKind().equals(DOUBLE); @@ -303,9 +298,7 @@ public Value emitUMulHigh(Value a, Value b) { switch (((SPARCKind) a.getPlatformKind())) { case WORD: - Value aExtended = emitBinary(LIRKind.combine(a), Srl, a, 0); - Value bExtended = emitBinary(LIRKind.combine(b), Srl, b, 0); - Value result = emitBinary(LIRKind.combine(a, b), Mulx, aExtended, bExtended); + Value result = emitBinary(LIRKind.combine(a, b), Mulx, emitZeroExtend(a), emitZeroExtend(b)); return emitBinary(LIRKind.combine(a, b), Srax, result, WORD.getSizeInBits()); case XWORD: return emitBinary(LIRKind.combine(a, b), UMulxhi, a, b); @@ -324,17 +317,13 @@ @Override public Value emitDiv(Value a, Value b, LIRFrameState state) { LIRKind resultKind = LIRKind.combine(a, b); - PlatformKind aKind = a.getPlatformKind(); - PlatformKind bKind = b.getPlatformKind(); if (isJavaConstant(b) && asJavaConstant(b).isDefaultForKind()) { // Div by zero Value zero = SPARC.g0.asValue(LIRKind.value(SPARCKind.WORD)); return emitBinary(resultKind, Op3s.Sdivx, zero, zero, state); - } else if (isNumericInteger(aKind)) { - Value fixedA = emitSignExtend(a, aKind.getSizeInBytes() * 8, 64); - Value fixedB = emitSignExtend(b, bKind.getSizeInBytes() * 8, 64); - return emitBinary(resultKind, Op3s.Sdivx, fixedA, fixedB, state); + } else if (isNumericInteger(a.getPlatformKind())) { + return emitBinary(resultKind, Op3s.Sdivx, emitSignExtend(a), emitSignExtend(b), state); } else { - boolean isDouble = a.getPlatformKind().equals(DOUBLE); + boolean isDouble = a.getPlatformKind() == DOUBLE; return emitBinary(resultKind, isDouble ? Opfs.Fdivd : Opfs.Fdivs, a, b, state); } } @@ -342,24 +331,21 @@ @Override public Value emitRem(Value a, Value b, LIRFrameState state) { Variable result = getLIRGen().newVariable(LIRKind.combine(a, b)); - Value aLoaded; Variable q1; // Intermediate values Variable q2; - SPARCKind aKind = (SPARCKind) a.getPlatformKind(); - switch (aKind) { + switch ((SPARCKind) a.getPlatformKind()) { case WORD: // Sign extend a and b - Variable as = emitBinary(result.getValueKind(), Sra, a, g0.asValue(LIRKind.value(WORD))); - Variable bs = emitBinary(result.getValueKind(), Sra, b, g0.asValue(LIRKind.value(WORD))); + Value as = emitSignExtend(a); + Value bs = emitSignExtend(b); q1 = emitBinary(as.getValueKind(), Sdivx, as, bs, state); - q2 = emitBinary(q1.getValueKind(), Mulx, q1, bs); + q2 = emitBinary(as.getValueKind(), Mulx, q1, bs); result = emitSub(as, q2, false); break; case XWORD: - aLoaded = getLIRGen().load(a); // Reuse the loaded value - q1 = emitBinary(result.getValueKind(), Sdivx, aLoaded, b, state); + q1 = emitBinary(result.getValueKind(), Sdivx, a, b, state); q2 = emitBinary(result.getValueKind(), Mulx, q1, b); - result = emitSub(aLoaded, q2, false); + result = emitSub(a, q2, false); break; case SINGLE: ForeignCallLinkage fremCall = getLIRGen().getForeignCalls().lookupForeignCall(ARITHMETIC_FREM); @@ -391,26 +377,14 @@ default: throw GraalError.shouldNotReachHere(); } - getLIRGen().append(new RemOp(opcode, result, getLIRGen().load(a), getLIRGen().load(b), scratch1, scratch2, state)); + getLIRGen().append(new RemOp(opcode, result, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b), scratch1, scratch2, state)); return result; } @Override public Value emitUDiv(Value a, Value b, LIRFrameState state) { - Value actualA = a; - Value actualB = b; - switch (((SPARCKind) a.getPlatformKind())) { - case WORD: - actualA = emitZeroExtend(actualA, 32, 64); - actualB = emitZeroExtend(actualB, 32, 64); - break; - case XWORD: - break; - default: - throw GraalError.shouldNotReachHere(); - } - return emitBinary(LIRKind.combine(actualA, actualB), Udivx, actualA, actualB, state); + return emitBinary(LIRKind.combine(a, b), Udivx, emitZeroExtend(a), emitZeroExtend(b), state); } @Override @@ -601,6 +575,11 @@ } } + private Value emitSignExtend(Value inputValue) { + int inputBits = inputValue.getPlatformKind().getSizeInBytes() * 8; + return emitNarrow(emitSignExtend(inputValue, inputBits, XWORD.getSizeInBits()), inputBits); + } + @Override public Value emitSignExtend(Value inputVal, int fromBits, int toBits) { assert fromBits <= toBits && toBits <= XWORD.getSizeInBits(); @@ -632,6 +611,11 @@ } } + private Value emitZeroExtend(Value inputValue) { + int inputBits = inputValue.getPlatformKind().getSizeInBytes() * 8; + return emitNarrow(emitZeroExtend(inputValue, inputBits, XWORD.getSizeInBits()), inputBits); + } + @Override public Value emitZeroExtend(Value inputValue, int fromBits, int toBits) { assert fromBits <= toBits && toBits <= 64; diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/HashCodeTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,18 +22,18 @@ */ package org.graalvm.compiler.core.test; -import org.junit.Assert; -import org.junit.Test; - import org.graalvm.compiler.core.phases.HighTier; import org.graalvm.compiler.core.phases.MidTier; import org.graalvm.compiler.nodes.InvokeNode; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.extended.LoadHubNode; import org.graalvm.compiler.nodes.extended.LoadMethodNode; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.tiers.MidTierContext; +import org.junit.Assert; +import org.junit.Test; public class HashCodeTest extends GraalCompilerTest { @@ -144,7 +144,9 @@ private void checkForGuardedIntrinsicPattern(String name) { StructuredGraph g = parseForCompile(getResolvedJavaMethod(name)); - Assert.assertEquals(1, g.getNodes().filter(InvokeNode.class).count()); + int invokeNodeCount = g.getNodes().filter(InvokeNode.class).count(); + int invokeWithExceptionNodeCount = g.getNodes().filter(InvokeWithExceptionNode.class).count(); + Assert.assertEquals(1, invokeNodeCount + invokeWithExceptionNodeCount); Assert.assertEquals(1, g.getNodes().filter(LoadHubNode.class).count()); Assert.assertEquals(1, g.getNodes().filter(LoadMethodNode.class).count()); } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/RethrowDeoptMaterializeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/RethrowDeoptMaterializeTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -0,0 +1,77 @@ +/* + * 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.deopt; + +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.FrameState; +import org.junit.Test; + +public final class RethrowDeoptMaterializeTest extends GraalCompilerTest { + + private static final Object RETURN_VALUE = "1 2 3"; + private static final RuntimeException DUMMY_EXCEPTION = new RuntimeException(); + + static class MyException extends RuntimeException { + private static final long serialVersionUID = 0L; + + MyException(Throwable cause) { + super(cause); + } + + @SuppressWarnings("sync-override") + @Override + public final Throwable fillInStackTrace() { + return null; + } + } + + public static Object executeDeoptRethrow(int action) { + + try { + if (action != 0) { + throw new MyException(DUMMY_EXCEPTION); + } else if (action == 1) { + throw new MyException(null); + } + } catch (RuntimeException t) { + Throwable e = t.getCause(); + GraalDirectives.deoptimize(); + if (e != DUMMY_EXCEPTION) { + throw t; + } + } + return RETURN_VALUE; + } + + /** + * This tests that a state with {@link FrameState#rethrowException()} set to true can properly + * throw an exception that must be rematerialized. + */ + @Test + public void testDeoptRethrow() { + test("executeDeoptRethrow", 1); + } +} diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Debug.java Thu Jun 01 02:21:18 2017 +0000 @@ -770,7 +770,7 @@ * @param object object to verify * @param message description of verification context * - * @see DebugVerifyHandler#verify(Object, String) + * @see DebugVerifyHandler#verify(java.lang.Object, java.lang.String, java.lang.Object...) */ public static void verify(Object object, String message) { if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) { @@ -786,7 +786,7 @@ * @param format a format string for the description of the verification context * @param arg the argument referenced by the format specifiers in {@code format} * - * @see DebugVerifyHandler#verify(Object, String) + * @see DebugVerifyHandler#verify(java.lang.Object, java.lang.String, java.lang.Object...) */ public static void verify(Object object, String format, Object arg) { if (ENABLED && DebugScope.getInstance().isVerifyEnabled()) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugDumpHandler.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugDumpHandler.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugDumpHandler.java Thu Jun 01 02:21:18 2017 +0000 @@ -26,7 +26,7 @@ public interface DebugDumpHandler extends Closeable { - void dump(Object object, String message); + void dump(Object object, String format, Object... arguments); /** * Add arbitrary capability for use by the handler. @@ -38,8 +38,8 @@ /** * Flushes and releases resources managed by this dump handler. A subsequent call to - * {@link #dump(Object, String)} will create and open new resources. That is, this method can be - * used to reset the handler. + * {@link #dump(java.lang.Object, java.lang.String, java.lang.Object...)} will create and open + * new resources. That is, this method can be used to reset the handler. */ @Override void close(); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugEnvironment.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugEnvironment.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugEnvironment.java Thu Jun 01 02:21:18 2017 +0000 @@ -70,7 +70,7 @@ return null; } GraalDebugConfig debugConfig = (GraalDebugConfig) DebugScope.getConfig(); - if (debugConfig == null || forceInit) { + if (debugConfig == null || forceInit || options != debugConfig.getOptions()) { // Initialize JVMCI before loading class Debug JVMCI.initialize(); List dumpHandlers = new ArrayList<>(); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugVerifyHandler.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugVerifyHandler.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugVerifyHandler.java Thu Jun 01 02:21:18 2017 +0000 @@ -31,7 +31,8 @@ * Verifies that a given object satisfies some invariants. * * @param object object to verify - * @param message description of verification context + * @param format description of verification context + * @param args arguments for the format */ - void verify(Object object, String message); + void verify(Object object, String format, Object... args); } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Fingerprint.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Fingerprint.java Wed May 31 09:19:28 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.graalvm.compiler.debug; - -import static org.graalvm.compiler.debug.Debug.DEBUG_OPTIONS; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import org.graalvm.compiler.options.Option; -import org.graalvm.compiler.options.OptionKey; - -/** - * Facility for fingerprinting execution. - */ -public class Fingerprint implements AutoCloseable { - - public static class Options { - @Option(help = "Enables execution fingerprinting.")// - public static final OptionKey UseFingerprinting = new OptionKey<>(false); - - @Option(help = "Limit number of events shown in fingerprinting error message.")// - public static final OptionKey FingerprintErrorEventTailLength = new OptionKey<>(50); - - @Option(help = "Fingerprinting event at which to execute breakpointable code.")// - public static final OptionKey FingerprintingBreakpointEvent = new OptionKey<>(-1); - } - - /** - * Determines whether fingerprinting is enabled. - */ - public static final boolean ENABLED = Options.UseFingerprinting.getValue(DEBUG_OPTIONS); - - private static final ThreadLocal current = ENABLED ? new ThreadLocal<>() : null; - - private final List events; - private int index; - - /** - * Creates an object to record a fingerprint. - */ - public Fingerprint() { - events = new ArrayList<>(); - index = -1; - } - - /** - * Creates an object to verify execution matches a given fingerprint. - * - * @param toVerifyAgainst the fingerprint events to verify against - */ - public Fingerprint(List toVerifyAgainst) { - this.events = toVerifyAgainst; - index = 0; - } - - /** - * Creates an object to verify execution matches a given fingerprint. - * - * @param toVerifyAgainst the fingerprint to verify against - */ - public Fingerprint(Fingerprint toVerifyAgainst) { - this(toVerifyAgainst.events); - } - - public Collection getEvents() { - return Collections.unmodifiableCollection(events); - } - - /** - * Starts fingerprint recording or verification for the current thread. At most one fingerprint - * object can be active for any thread. - */ - public Fingerprint open() { - if (ENABLED) { - assert current.get() == null; - current.set(this); - return this; - } - return null; - } - - /** - * Finishes fingerprint recording or verification for the current thread. - */ - @Override - public void close() { - if (ENABLED) { - assert current.get() == this; - current.set(null); - } - } - - private static final int BREAKPOINT_EVENT = Options.FingerprintingBreakpointEvent.getValue(DEBUG_OPTIONS); - - /** - * Submits an execution event for the purpose of recording or verifying a fingerprint. This must - * only be called if {@link #ENABLED} is {@code true}. - */ - public static void submit(String format, Object... args) { - assert ENABLED : "fingerprinting must be enabled (-Dgraal." + Options.UseFingerprinting.getName() + "=true)"; - Fingerprint fingerprint = current.get(); - if (fingerprint != null) { - int eventId = fingerprint.nextEventId(); - if (eventId == BREAKPOINT_EVENT) { - // Set IDE breakpoint on the following line and set the relevant - // system property to debug a fingerprint verification error. - System.console(); - } - fingerprint.event(String.format(eventId + ": " + format, args)); - } - } - - private int nextEventId() { - return index == -1 ? events.size() : index; - } - - private static final int MAX_EVENT_TAIL_IN_ERROR_MESSAGE = Options.FingerprintErrorEventTailLength.getValue(DEBUG_OPTIONS); - - private String tail() { - int start = Math.max(index - MAX_EVENT_TAIL_IN_ERROR_MESSAGE, 0); - return events.subList(start, index).stream().collect(Collectors.joining(String.format("%n"))); - } - - private void event(String entry) { - if (index == -1) { - events.add(entry); - } else { - if (index > events.size()) { - throw new InternalError(String.format("%s%nOriginal fingerprint limit reached", tail())); - } - String l = events.get(index); - if (!l.equals(entry)) { - throw new InternalError(String.format("%s%nFingerprint differs at event %d%nexpected: %s%n actual: %s", tail(), index, l, entry)); - } - index++; - } - } -} diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/internal/DebugScope.java Thu Jun 01 02:21:18 2017 +0000 @@ -333,9 +333,8 @@ if (isDumpEnabled(dumpLevel)) { DebugConfig config = getConfig(); if (config != null) { - String message = String.format(formatString, args); for (DebugDumpHandler dumpHandler : config.dumpHandlers()) { - dumpHandler.dump(object, message); + dumpHandler.dump(object, formatString, args); } } } @@ -347,9 +346,8 @@ public static void forceDump(Object object, String format, Object... args) { DebugConfig config = getConfig(); if (config != null) { - String message = String.format(format, args); for (DebugDumpHandler dumpHandler : config.dumpHandlers()) { - dumpHandler.dump(object, message); + dumpHandler.dump(object, format, args); } } else { TTY.println("Forced dump ignored because debugging is disabled - use -Dgraal.ForceDebugEnable=true"); @@ -363,9 +361,8 @@ if (isVerifyEnabled()) { DebugConfig config = getConfig(); if (config != null) { - String message = String.format(formatString, args); for (DebugVerifyHandler handler : config.verifyHandlers()) { - handler.verify(object, message); + handler.verify(object, formatString, args); } } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/.checkstyle_checks.xml --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/.checkstyle_checks.xml Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/.checkstyle_checks.xml Thu Jun 01 02:21:18 2017 +0000 @@ -161,7 +161,8 @@ - + + diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,19 +22,10 @@ */ package org.graalvm.compiler.graph; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.function.Consumer; - import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.DebugCloseable; import org.graalvm.compiler.debug.DebugCounter; import org.graalvm.compiler.debug.DebugTimer; -import org.graalvm.compiler.debug.Fingerprint; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node.ValueNumberable; import org.graalvm.compiler.graph.iterators.NodeIterable; @@ -46,6 +37,14 @@ import org.graalvm.util.Equivalence; import org.graalvm.util.UnmodifiableEconomicMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.function.Consumer; + +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; + /** * This class is a graph container, it contains the set of nodes that belong to this graph. */ @@ -982,9 +981,6 @@ if (nodeEventListener != null) { nodeEventListener.nodeAdded(node); } - if (Fingerprint.ENABLED) { - Fingerprint.submit("%s: %s", NodeEvent.NODE_ADDED, node); - } afterRegister(node); } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java Thu Jun 01 02:21:18 2017 +0000 @@ -43,8 +43,6 @@ import org.graalvm.compiler.core.common.type.AbstractPointerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.DebugCloseable; -import org.graalvm.compiler.debug.Fingerprint; -import org.graalvm.compiler.graph.Graph.NodeEvent; import org.graalvm.compiler.graph.Graph.NodeEventListener; import org.graalvm.compiler.graph.Graph.Options; import org.graalvm.compiler.graph.iterators.NodeIterable; @@ -738,9 +736,6 @@ if (listener != null) { listener.inputChanged(node); } - if (Fingerprint.ENABLED) { - Fingerprint.submit("%s: %s", NodeEvent.INPUT_CHANGED, node); - } } } @@ -751,9 +746,6 @@ if (listener != null && node.isAlive()) { listener.usagesDroppedToZero(node); } - if (Fingerprint.ENABLED) { - Fingerprint.submit("%s: %s", NodeEvent.ZERO_USAGES, node); - } } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeClass.java Thu Jun 01 02:21:18 2017 +0000 @@ -49,7 +49,6 @@ import org.graalvm.compiler.debug.DebugCloseable; import org.graalvm.compiler.debug.DebugCounter; import org.graalvm.compiler.debug.DebugTimer; -import org.graalvm.compiler.debug.Fingerprint; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Edges.Type; import org.graalvm.compiler.graph.Graph.DuplicationReplacement; @@ -810,10 +809,11 @@ /** * The template used to build the {@link Verbosity#Name} version. Variable parts are specified - * using {i#inputName} or {p#propertyName}. + * using {i#inputName} or {p#propertyName}. Returns empty string if no + * special name template is specified. */ public String getNameTemplate() { - return nameTemplate.isEmpty() ? shortName() : nameTemplate; + return nameTemplate; } interface InplaceUpdateClosure { @@ -879,15 +879,9 @@ replacement = replacements.replacement(node); } if (replacement != node) { - if (Fingerprint.ENABLED) { - Fingerprint.submit("replacing %s with %s", node, replacement); - } assert replacement != null; newNodes.put(node, replacement); } else { - if (Fingerprint.ENABLED) { - Fingerprint.submit("duplicating %s", node); - } Node newNode = node.clone(graph, WithAllEdges); assert newNode.getNodeClass().isLeafNode() || newNode.hasNoUsages(); assert newNode.getClass() == node.getClass(); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalMBeanTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalMBeanTest.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotGraalMBeanTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,17 +24,21 @@ import java.lang.management.ManagementFactory; import java.lang.reflect.Field; +import javax.management.Attribute; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanServer; import javax.management.ObjectInstance; import javax.management.ObjectName; import org.graalvm.compiler.hotspot.HotSpotGraalMBean; -import static org.junit.Assert.assertFalse; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.util.EconomicMap; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import org.junit.Test; public class HotSpotGraalMBeanTest { @@ -80,18 +84,73 @@ HotSpotGraalMBean realBean = HotSpotGraalMBean.create(); assertNotNull("Bean is registered", name = realBean.ensureRegistered(false)); + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - ObjectInstance bean = ManagementFactory.getPlatformMBeanServer().getObjectInstance(name); + ObjectInstance bean = server.getObjectInstance(name); assertNotNull("Bean is registered", bean); - MBeanInfo info = ManagementFactory.getPlatformMBeanServer().getMBeanInfo(name); + MBeanInfo info = server.getMBeanInfo(name); assertNotNull("Info is found", info); + + MBeanAttributeInfo printCompilation = findAttributeInfo("PrintCompilation", info); + assertNotNull("PrintCompilation found", printCompilation); + assertEquals("true/false", Boolean.class.getName(), printCompilation.getType()); + + Attribute printOn = new Attribute(printCompilation.getName(), Boolean.TRUE); + + Object before = server.getAttribute(name, printCompilation.getName()); + server.setAttribute(name, printOn); + Object after = server.getAttribute(name, printCompilation.getName()); + + assertNull("Default value was not set", before); + assertEquals("Changed to on", Boolean.TRUE, after); + } + + private static MBeanAttributeInfo findAttributeInfo(String attrName, MBeanInfo info) { + MBeanAttributeInfo printCompilation = null; for (MBeanAttributeInfo attr : info.getAttributes()) { - if (attr.getName().equals("Dump")) { - assertFalse("Read only now", attr.isWritable()); + if (attr.getName().equals(attrName)) { assertTrue("Readable", attr.isReadable()); - return; + assertTrue("Writable", attr.isWritable()); + printCompilation = attr; + break; } } - fail("No Dump attribute found"); + return printCompilation; } + + @Test + public void optionsAreCached() throws Exception { + ObjectName name; + + assertNotNull("Server is started", ManagementFactory.getPlatformMBeanServer()); + + HotSpotGraalMBean realBean = HotSpotGraalMBean.create(); + + OptionValues original = new OptionValues(EconomicMap.create()); + + assertSame(original, realBean.optionsFor(original, null)); + + assertNotNull("Bean is registered", name = realBean.ensureRegistered(false)); + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + + ObjectInstance bean = server.getObjectInstance(name); + assertNotNull("Bean is registered", bean); + MBeanInfo info = server.getMBeanInfo(name); + assertNotNull("Info is found", info); + + MBeanAttributeInfo dump = findAttributeInfo("Dump", info); + + Attribute dumpTo1 = new Attribute(dump.getName(), 1); + + server.setAttribute(name, dumpTo1); + Object after = server.getAttribute(name, dump.getName()); + assertEquals(1, after); + + final OptionValues modified1 = realBean.optionsFor(original, null); + assertNotSame(original, modified1); + final OptionValues modified2 = realBean.optionsFor(original, null); + assertSame("Options are cached", modified1, modified2); + + } + } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ObjectCloneTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ObjectCloneTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -0,0 +1,87 @@ +/* + * 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.hotspot.test; + +import java.util.ArrayList; + +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.junit.Test; + +/** + * Exercise intrinsification of {@link Object#clone}. + */ +public class ObjectCloneTest extends GraalCompilerTest { + + public static Object cloneArray(int[] array) { + return array.clone(); + } + + public static Object cloneList(ArrayList list) { + return list.clone(); + } + + static class ObjectCloneable implements Cloneable { + int field; + + @Override + protected Object clone() throws CloneNotSupportedException { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + } + + static class CloneableSubclass extends ObjectCloneable { + + } + + /* + * This test checks that the ObjectCloneNode doesn't accidentally inject non-nullness into the + * graph which is later removed. + */ + public static Object notCloneable(ObjectCloneable cloneable) throws CloneNotSupportedException { + ObjectCloneable clone = (ObjectCloneable) cloneable.clone(); + return clone.getClass(); + } + + @Test + public void testNotIntrinsified() throws Throwable { + test("notCloneable", new CloneableSubclass()); + } + + @Test + public void testArray() throws Throwable { + test("cloneArray", new int[]{1, 2, 4, 3}); + } + + @Test + public void testList() throws Throwable { + ArrayList list = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + list.add(i); + } + test("cloneList", list); + } +} diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/OptionsInFileTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/OptionsInFileTest.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/OptionsInFileTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -51,7 +51,7 @@ File optionsFile = File.createTempFile("options", ".properties").getAbsoluteFile(); try { Assert.assertFalse(methodFilterValue.equals(MethodFilter.getDefaultValue())); - Assert.assertFalse(debugFilterValue.equals(PrintGraph.getDefaultValue())); + Assert.assertFalse(debugFilterValue.equals(Dump.getDefaultValue())); Assert.assertTrue(PrintGraph.getDefaultValue()); try (PrintStream out = new PrintStream(new FileOutputStream(optionsFile))) { @@ -61,6 +61,7 @@ } List vmArgs = withoutDebuggerArguments(getVMCommandLine()); + vmArgs.removeIf(a -> a.startsWith("-Dgraal.")); vmArgs.add("-Dgraal.options.file=" + optionsFile); vmArgs.add("-XX:+JVMCIPrintProperties"); Subprocess proc = SubprocessUtil.java(vmArgs); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/RetryableCompilationTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -63,6 +63,9 @@ private static void testHelper(List extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException { List vmArgs = withoutDebuggerArguments(getVMCommandLine()); + vmArgs.removeIf(a -> a.startsWith("-Dgraal.")); + // Force output to a file even if there's a running IGV instance available. + vmArgs.add("-Dgraal.PrintGraphFile=true"); vmArgs.addAll(extraVmArgs); Subprocess proc = SubprocessUtil.java(vmArgs, mainClassAndArgs); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotDebugInfoBuilder.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,8 +24,14 @@ import static jdk.vm.ci.code.BytecodeFrame.isPlaceholderBci; +import java.util.ArrayList; +import java.util.List; + +import org.graalvm.compiler.api.replacements.MethodSubstitution; +import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.core.gen.DebugInfoBuilder; -import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.GraalGraphError; +import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.lir.VirtualStackSlot; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.ValueNode; @@ -36,6 +42,8 @@ import jdk.vm.ci.code.VirtualObject; import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; import jdk.vm.ci.meta.JavaValue; +import jdk.vm.ci.meta.MetaUtil; +import jdk.vm.ci.meta.ResolvedJavaMethod; /** * Extends {@link DebugInfoBuilder} to allocate the extra debug information required for locks. @@ -79,11 +87,40 @@ @Override protected BytecodeFrame computeFrameForState(FrameState state) { if (isPlaceholderBci(state.bci) && state.bci != BytecodeFrame.BEFORE_BCI) { - // This is really a hard error since an incorrect state could crash hotspot - throw GraalError.shouldNotReachHere("Invalid state " + BytecodeFrame.getPlaceholderBciName(state.bci) + " " + state); + raiseInvalidFrameStateError(state); } BytecodeFrame result = super.computeFrameForState(state); maxInterpreterFrameSize = Math.max(maxInterpreterFrameSize, codeCacheProvider.interpreterFrameSize(result)); return result; } + + protected void raiseInvalidFrameStateError(FrameState state) throws GraalGraphError { + // This is a hard error since an incorrect state could crash hotspot + NodeSourcePosition sourcePosition = state.getNodeSourcePosition(); + List context = new ArrayList<>(); + ResolvedJavaMethod replacementMethodWithProblematicSideEffect = null; + if (sourcePosition != null) { + NodeSourcePosition pos = sourcePosition; + while (pos != null) { + StringBuilder sb = new StringBuilder("parsing "); + ResolvedJavaMethod method = pos.getMethod(); + MetaUtil.appendLocation(sb, method, pos.getBCI()); + if (method.getAnnotation(MethodSubstitution.class) != null || + method.getAnnotation(Snippet.class) != null) { + replacementMethodWithProblematicSideEffect = method; + } + context.add(sb.toString()); + pos = pos.getCaller(); + } + } + String message = "Invalid frame state " + state; + if (replacementMethodWithProblematicSideEffect != null) { + message += " associated with a side effect in " + replacementMethodWithProblematicSideEffect.format("%H.%n(%p)") + " at a position that cannot be deoptimized to"; + } + GraalGraphError error = new GraalGraphError(message); + for (String c : context) { + error.addContext(c); + } + throw error; + } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalMBean.java Thu Jun 01 02:21:18 2017 +0000 @@ -25,14 +25,12 @@ import java.lang.management.ManagementFactory; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.InstanceAlreadyExistsException; -import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; @@ -49,12 +47,14 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.options.OptionsParser; import org.graalvm.util.EconomicMap; +import org.graalvm.util.UnmodifiableEconomicMap; public final class HotSpotGraalMBean implements DynamicMBean { private static Object mBeanServerField; private final OptionValues options; private final EconomicMap, Object> changes; private ObjectName registered; + private OptionValues cachedOptions; private HotSpotGraalMBean(OptionValues options) { this.options = options; @@ -112,27 +112,51 @@ } @SuppressWarnings("unused") - OptionValues optionsFor(OptionValues values, ResolvedJavaMethod forMethod) { + public OptionValues optionsFor(OptionValues initialValues, ResolvedJavaMethod forMethod) { ensureRegistered(true); + return currentMap(initialValues); + } + + private OptionValues currentMap(OptionValues initialValues) { if (changes.isEmpty()) { - return values; + return initialValues; } - return new OptionValues(values, changes); + OptionValues current = cachedOptions; + if (current == null) { + current = new OptionValues(initialValues, changes); + cachedOptions = current; + } + return current; } @Override public Object getAttribute(String attribute) { - for (OptionKey k : options.getMap().getKeys()) { + UnmodifiableEconomicMap, Object> map = currentMap(options).getMap(); + for (OptionKey k : map.getKeys()) { if (k.getName().equals(attribute)) { - return options.getMap().get(k); + return map.get(k); } } return null; } @Override - public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { - throw new InvalidAttributeValueException(); + public void setAttribute(Attribute attribute) throws AttributeNotFoundException { + Attribute newAttr = setImpl(attribute); + if (newAttr == null) { + throw new AttributeNotFoundException(); + } + } + + private Attribute setImpl(Attribute attribute) { + cachedOptions = null; + for (OptionDescriptor option : allOptionDescriptors()) { + if (option.getName().equals(attribute.getName())) { + changes.put(option.getOptionKey(), attribute.getValue()); + return attribute; + } + } + return null; } @Override @@ -149,7 +173,14 @@ @Override public AttributeList setAttributes(AttributeList attributes) { - throw new IllegalStateException(); + AttributeList setOk = new AttributeList(); + for (Attribute attr : attributes.asList()) { + Attribute newAttr = setImpl(attr); + if (newAttr != null) { + setOk.add(newAttr); + } + } + return setOk; } @Override @@ -161,10 +192,8 @@ public MBeanInfo getMBeanInfo() { List attrs = new ArrayList<>(); if (registered != null) { - for (Iterator it = OptionsParser.getOptionsLoader().iterator(); it.hasNext();) { - for (OptionDescriptor descr : it.next()) { - attrs.add(new MBeanAttributeInfo(descr.getName(), descr.getType().getName(), descr.getHelp(), true, false, false)); - } + for (OptionDescriptor descr : allOptionDescriptors()) { + attrs.add(new MBeanAttributeInfo(descr.getName(), descr.getType().getName(), descr.getHelp(), true, true, false)); } } return new MBeanInfo( @@ -174,4 +203,14 @@ null, null, null); } + private static Iterable allOptionDescriptors() { + List arr = new ArrayList<>(); + for (OptionDescriptors set : OptionsParser.getOptionsLoader()) { + for (OptionDescriptor descr : set) { + arr.add(descr); + } + } + return arr; + } + } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/NewObjectSnippets.java Thu Jun 01 02:21:18 2017 +0000 @@ -88,7 +88,6 @@ import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyFixedNode; import org.graalvm.compiler.hotspot.nodes.aot.LoadConstantIndirectlyNode; import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp; -import org.graalvm.compiler.hotspot.replacements.aot.ResolveConstantSnippets; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizeNode; @@ -299,7 +298,7 @@ public static Object allocateArrayPIC(KlassPointer hub, int length, Word prototypeMarkWord, @ConstantParameter int headerSize, @ConstantParameter int log2ElementSize, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter boolean maybeUnroll, @ConstantParameter String typeContext, @ConstantParameter OptionValues options, @ConstantParameter Counters counters) { - KlassPointer picHub = ResolveConstantSnippets.resolveKlassConstant(hub); + KlassPointer picHub = LoadConstantIndirectlyFixedNode.loadKlass(hub); return allocateArrayImpl(picHub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext, false, options, counters); } @@ -415,8 +414,8 @@ @Snippet public static Object newmultiarrayPIC(KlassPointer hub, @ConstantParameter int rank, @VarargsParameter int[] dimensions) { - KlassPointer hubPIC = ResolveConstantSnippets.resolveKlassConstant(hub); - return newmultiarray(hubPIC, rank, dimensions); + KlassPointer picHub = LoadConstantIndirectlyFixedNode.loadKlass(hub); + return newmultiarray(picHub, rank, dimensions); } @NodeIntrinsic(value = ForeignCallNode.class, injectedStampIsNonNull = true) diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,6 +24,8 @@ import java.lang.reflect.Method; +import org.graalvm.compiler.core.common.type.AbstractPointerStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampPair; import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.Debug.Scope; @@ -60,6 +62,18 @@ } @Override + protected Stamp computeStamp(ValueNode object) { + if (getConcreteType(object.stamp()) != null) { + return AbstractPointerStamp.pointerNonNull(object.stamp()); + } + /* + * If this call can't be intrinsified don't report a non-null stamp, otherwise the stamp + * would change when this is lowered back to an invoke and we might lose a null check. + */ + return AbstractPointerStamp.pointerMaybeNull(object.stamp()); + } + + @Override @SuppressWarnings("try") protected StructuredGraph getLoweredSnippetGraph(LoweringTool tool) { ResolvedJavaType type = StampTool.typeOrNull(getObject()); @@ -77,6 +91,7 @@ } assert snippetGraph != null : "ObjectCloneSnippets should be installed"; + assert getConcreteType(stamp()) != null; return lowerReplacement((StructuredGraph) snippetGraph.copy(), tool); } assert false : "unhandled array type " + type.getComponentType().getJavaKind(); @@ -96,10 +111,12 @@ newGraph.addBeforeFixed(returnNode, load); newGraph.addBeforeFixed(returnNode, newGraph.add(new StoreFieldNode(newInstance, field, load))); } + assert getConcreteType(stamp()) != null; return lowerReplacement(newGraph, tool); } } } + assert getConcreteType(stamp()) == null; return null; } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java Thu Jun 01 02:21:18 2017 +0000 @@ -248,6 +248,7 @@ import static org.graalvm.compiler.debug.GraalError.guarantee; import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere; import static org.graalvm.compiler.java.BytecodeParserOptions.DumpWithInfopoints; +import static org.graalvm.compiler.java.BytecodeParserOptions.InlinePartialIntrinsicExitDuringParsing; import static org.graalvm.compiler.java.BytecodeParserOptions.TraceBytecodeParserLevel; import static org.graalvm.compiler.java.BytecodeParserOptions.TraceInlineDuringParsing; import static org.graalvm.compiler.java.BytecodeParserOptions.TraceParserPlugins; @@ -264,6 +265,7 @@ import java.util.List; import org.graalvm.api.word.LocationIdentity; +import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.bytecode.Bytecode; import org.graalvm.compiler.bytecode.BytecodeDisassembler; import org.graalvm.compiler.bytecode.BytecodeLookupSwitch; @@ -463,6 +465,7 @@ FrameState stateBefore; final Mark mark; final BytecodeParser parser; + List returnDataList; /** * Creates a scope for root parsing an intrinsic. @@ -504,46 +507,50 @@ * added to the graph while parsing/inlining the intrinsic for which this object exists. */ private void processPlaceholderFrameStates(IntrinsicContext intrinsic) { - FrameState stateAfterReturn = null; StructuredGraph graph = parser.getGraph(); for (Node node : graph.getNewNodes(mark)) { if (node instanceof FrameState) { FrameState frameState = (FrameState) node; if (BytecodeFrame.isPlaceholderBci(frameState.bci)) { if (frameState.bci == BytecodeFrame.AFTER_BCI) { - FrameStateBuilder frameStateBuilder = parser.frameState; - if (frameState.stackSize() != 0) { - assert frameState.usages().count() == 1; - ValueNode returnVal = frameState.stackAt(0); - assert returnVal == frameState.usages().first(); - - if (parser.currentInvokeReturnType == null) { - assert intrinsic.isCompilationRoot(); - FrameState newFrameState = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); - frameState.replaceAndDelete(newFrameState); - } else { - /* - * Swap the top-of-stack value with the side-effect return value - * using the frame state. - */ - JavaKind returnKind = parser.currentInvokeReturnType.getJavaKind(); + if (parser.getInvokeReturnType() == null) { + // A frame state in a root compiled intrinsic. + assert intrinsic.isCompilationRoot(); + FrameState newFrameState = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); + frameState.replaceAndDelete(newFrameState); + } else { + JavaKind returnKind = parser.getInvokeReturnType().getJavaKind(); + FrameStateBuilder frameStateBuilder = parser.frameState; + assert !frameState.rethrowException(); + if (frameState.stackSize() != 0) { + ValueNode returnVal = frameState.stackAt(0); + if (!ReturnToCallerData.containsReturnValue(returnDataList, returnVal)) { + throw new GraalError("AFTER_BCI frame state within an intrinsic has a non-return value on the stack: %s", returnVal); + } + + // Swap the top-of-stack value with the return value ValueNode tos = frameStateBuilder.pop(returnKind); assert tos.getStackKind() == returnVal.getStackKind(); FrameState newFrameState = frameStateBuilder.create(parser.stream.nextBCI(), parser.getNonIntrinsicAncestor(), false, new JavaKind[]{returnKind}, new ValueNode[]{returnVal}); frameState.replaceAndDelete(newFrameState); + newFrameState.setNodeSourcePosition(frameState.getNodeSourcePosition()); frameStateBuilder.push(returnKind, tos); + } else if (returnKind != JavaKind.Void) { + // If the intrinsic returns a non-void value, then any frame + // state with an empty stack is invalid as it cannot + // be used to deoptimize to just after the call returns. + // These invalid frame states are expected to be removed + // by later compilation stages. + FrameState newFrameState = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); + newFrameState.setNodeSourcePosition(frameState.getNodeSourcePosition()); + frameState.replaceAndDelete(newFrameState); + } else { + // An intrinsic for a void method. + FrameState newFrameState = frameStateBuilder.create(parser.stream.nextBCI(), null); + newFrameState.setNodeSourcePosition(frameState.getNodeSourcePosition()); + frameState.replaceAndDelete(newFrameState); } - } else { - if (stateAfterReturn == null) { - if (intrinsic != null) { - assert intrinsic.isCompilationRoot(); - stateAfterReturn = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI)); - } else { - stateAfterReturn = frameStateBuilder.create(parser.stream.nextBCI(), null); - } - } - frameState.replaceAndDelete(stateAfterReturn); } } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) { if (stateBefore == null) { @@ -552,6 +559,24 @@ if (stateBefore != frameState) { frameState.replaceAndDelete(stateBefore); } + } else if (frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) { + // This is a frame state for the entry point to an exception + // dispatcher in an intrinsic. For example, the invoke denoting + // a partial intrinsic exit will have an edge to such a + // dispatcher if the profile for the original invoke being + // intrinsified indicates an exception was seen. As per JVM + // bytecode semantics, the interpreter expects a single + // value on the stack on entry to an exception handler, + // namely the exception object. + assert frameState.rethrowException(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) frameState.stackAt(0); + FrameStateBuilder dispatchState = parser.frameState.copy(); + dispatchState.clearStack(); + dispatchState.push(JavaKind.Object, exceptionObject); + dispatchState.setRethrowException(true); + FrameState newFrameState = dispatchState.create(parser.bci(), exceptionObject); + frameState.replaceAndDelete(newFrameState); + newFrameState.setNodeSourcePosition(frameState.getNodeSourcePosition()); } else { assert frameState.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI; } @@ -591,6 +616,15 @@ this.returnValue = returnValue; this.beforeReturnNode = beforeReturnNode; } + + static boolean containsReturnValue(List list, ValueNode value) { + for (ReturnToCallerData e : list) { + if (e.returnValue == value) { + return true; + } + } + return false; + } } private final GraphBuilderPhase.Instance graphBuilderInstance; @@ -1333,8 +1367,19 @@ } } - private InvokeKind currentInvokeKind; - private JavaType currentInvokeReturnType; + static class CurrentInvoke { + final ValueNode[] args; + final InvokeKind kind; + final JavaType returnType; + + CurrentInvoke(ValueNode[] args, InvokeKind kind, JavaType returnType) { + this.args = args; + this.kind = kind; + this.returnType = returnType; + } + } + + private CurrentInvoke currentInvoke; protected FrameStateBuilder frameState; protected BciBlock currentBlock; protected final BytecodeStream stream; @@ -1353,12 +1398,12 @@ @Override public InvokeKind getInvokeKind() { - return currentInvokeKind; + return currentInvoke == null ? null : currentInvoke.kind; } @Override public JavaType getInvokeReturnType() { - return currentInvokeReturnType; + return currentInvoke == null ? null : currentInvoke.returnType; } private boolean forceInliningEverything; @@ -1376,7 +1421,9 @@ @Override public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) { - createNonInlinedInvoke(bci(), callTarget, resultType, null); + BytecodeParser intrinsicCallSiteParser = getNonIntrinsicAncestor(); + boolean withExceptionEdge = intrinsicCallSiteParser == null ? false : intrinsicCallSiteParser.omitInvokeExceptionEdge(null); + createNonInlinedInvoke(withExceptionEdge, bci(), callTarget, resultType); } private Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) { @@ -1408,8 +1455,7 @@ InlineInfo inlineInfo = null; try { - currentInvokeReturnType = returnType; - currentInvokeKind = invokeKind; + currentInvoke = new CurrentInvoke(args, invokeKind, returnType); if (tryNodePluginForInvocation(args, targetMethod)) { if (TraceParserPlugins.getValue(options)) { traceWithContext("used node plugin for %s", targetMethod.format("%h.%n(%p)")); @@ -1438,15 +1484,35 @@ } } } finally { - currentInvokeReturnType = null; - currentInvokeKind = null; + currentInvoke = null; } - int bci = bci(); + int invokeBci = bci(); + JavaTypeProfile profile = getProfileForInvoke(invokeKind); + boolean withExceptionEdge = !omitInvokeExceptionEdge(inlineInfo); boolean partialIntrinsicExit = false; if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) { partialIntrinsicExit = true; ResolvedJavaMethod originalMethod = intrinsicContext.getOriginalMethod(); + BytecodeParser intrinsicCallSiteParser = getNonIntrinsicAncestor(); + if (intrinsicCallSiteParser != null) { + // When exiting a partial intrinsic, the invoke to the original + // must use the same context as the call to the intrinsic. + invokeBci = intrinsicCallSiteParser.bci(); + profile = intrinsicCallSiteParser.getProfileForInvoke(invokeKind); + withExceptionEdge = !intrinsicCallSiteParser.omitInvokeExceptionEdge(inlineInfo); + } else { + // We are parsing the intrinsic for the root compilation or for inlining, + // This call is a partial intrinsic exit, and we do not have profile information + // for this callsite. We also have to assume that the call needs an exception + // edge. Finally, we know that this intrinsic is parsed for late inlining, + // so the bci must be set to unknown, so that the inliner patches it later. + assert intrinsicContext.isPostParseInlined(); + invokeBci = BytecodeFrame.UNKNOWN_BCI; + profile = null; + withExceptionEdge = graph.method().getAnnotation(Snippet.class) == null; + } + if (originalMethod.isStatic()) { invokeKind = InvokeKind.Static; } else { @@ -1457,15 +1523,10 @@ Signature sig = originalMethod.getSignature(); returnType = sig.getReturnType(method.getDeclaringClass()); resultType = sig.getReturnKind(); - bci = intrinsicContext.bci(); - assert checkPartialIntrinsicExit(args); + assert checkPartialIntrinsicExit(intrinsicCallSiteParser == null ? null : intrinsicCallSiteParser.currentInvoke.args, args); targetMethod = originalMethod; } - JavaTypeProfile profile = null; - if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints(getOptions())) { - profile = profilingInfo.getTypeProfile(bci()); - } - Invoke invoke = createNonInlinedInvoke(args, bci, targetMethod, invokeKind, resultType, returnType, inlineInfo, profile); + Invoke invoke = createNonInlinedInvoke(withExceptionEdge, invokeBci, args, targetMethod, invokeKind, resultType, returnType, profile); if (partialIntrinsicExit) { // This invoke must never be later inlined as it might select the intrinsic graph. // Until there is a mechanism to guarantee that any late inlining will not select @@ -1475,24 +1536,30 @@ return invoke; } + protected JavaTypeProfile getProfileForInvoke(InvokeKind invokeKind) { + if (invokeKind.isIndirect() && profilingInfo != null && this.optimisticOpts.useTypeCheckHints(getOptions())) { + return profilingInfo.getTypeProfile(bci()); + } + return null; + } + /** * A partial intrinsic exits by (effectively) calling the intrinsified method. This call must * use exactly the arguments to the call being intrinsified. * - * @param args arguments of recursive call to intrinsified method + * @param originalArgs arguments of original call to intrinsified method + * @param recursiveArgs arguments of recursive call to intrinsified method */ - private boolean checkPartialIntrinsicExit(ValueNode[] args) { - if (intrinsicContext.getArgs() != null) { - assert intrinsicContext.bci() >= 0; - ValueNode[] icArgs = intrinsicContext.getArgs(); - for (int i = 0; i < icArgs.length; i++) { - ValueNode arg = GraphUtil.unproxify(args[i]); - ValueNode icArg = GraphUtil.unproxify(icArgs[i]); + private static boolean checkPartialIntrinsicExit(ValueNode[] originalArgs, ValueNode[] recursiveArgs) { + if (originalArgs != null) { + for (int i = 0; i < originalArgs.length; i++) { + ValueNode arg = GraphUtil.unproxify(recursiveArgs[i]); + ValueNode icArg = GraphUtil.unproxify(originalArgs[i]); assert arg == icArg : String.format("argument %d of call denoting partial intrinsic exit should be %s, not %s", i, icArg, arg); } } else { - for (int i = 0; i < args.length; i++) { - ValueNode arg = GraphUtil.unproxify(args[i]); + for (int i = 0; i < recursiveArgs.length; i++) { + ValueNode arg = GraphUtil.unproxify(recursiveArgs[i]); assert arg instanceof ParameterNode && ((ParameterNode) arg).index() == i : String.format("argument %d of call denoting partial intrinsic exit should be a %s with index %d, not %s", i, ParameterNode.class.getSimpleName(), i, arg); } @@ -1500,8 +1567,8 @@ return true; } - protected Invoke createNonInlinedInvoke(ValueNode[] invokeArgs, int invokeBci, ResolvedJavaMethod targetMethod, - InvokeKind invokeKind, JavaKind resultType, JavaType returnType, InlineInfo inlineInfo, JavaTypeProfile profile) { + protected Invoke createNonInlinedInvoke(boolean withExceptionEdge, int invokeBci, ValueNode[] invokeArgs, ResolvedJavaMethod targetMethod, + InvokeKind invokeKind, JavaKind resultType, JavaType returnType, JavaTypeProfile profile) { StampPair returnStamp = graphBuilderConfig.getPlugins().getOverridingStamp(this, returnType, false); if (returnStamp == null) { @@ -1509,7 +1576,7 @@ } MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, targetMethod, invokeArgs, returnStamp, profile)); - Invoke invoke = createNonInlinedInvoke(invokeBci, callTarget, resultType, inlineInfo); + Invoke invoke = createNonInlinedInvoke(withExceptionEdge, invokeBci, callTarget, resultType); for (InlineInvokePlugin plugin : graphBuilderConfig.getPlugins().getInlineInvokePlugins()) { plugin.notifyNotInlined(this, targetMethod, invoke); @@ -1518,8 +1585,8 @@ return invoke; } - protected Invoke createNonInlinedInvoke(int invokeBci, CallTargetNode callTarget, JavaKind resultType, InlineInfo inlineInfo) { - if (omitInvokeExceptionEdge(callTarget, inlineInfo)) { + protected Invoke createNonInlinedInvoke(boolean withExceptionEdge, int invokeBci, CallTargetNode callTarget, JavaKind resultType) { + if (!withExceptionEdge) { return createInvoke(invokeBci, callTarget, resultType); } else { Invoke invoke = createInvokeWithException(invokeBci, callTarget, resultType); @@ -1533,10 +1600,8 @@ /** * If the method returns true, the invocation of the given {@link MethodCallTargetNode call * target} does not need an exception edge. - * - * @param callTarget The call target. */ - protected boolean omitInvokeExceptionEdge(CallTargetNode callTarget, InlineInfo lastInlineInfo) { + protected boolean omitInvokeExceptionEdge(InlineInfo lastInlineInfo) { if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION) { return false; } else if (lastInlineInfo == InlineInfo.DO_NOT_INLINE_NO_EXCEPTION) { @@ -1551,8 +1616,17 @@ assert graphBuilderConfig.getBytecodeExceptionMode() == BytecodeExceptionMode.Profile; // be conservative if information was not recorded (could result in endless // recompiles otherwise) - return (!StressInvokeWithExceptionNode.getValue(options) && optimisticOpts.useExceptionProbability(getOptions()) && profilingInfo != null && - profilingInfo.getExceptionSeen(bci()) == TriState.FALSE); + if (!StressInvokeWithExceptionNode.getValue(options)) { + if (optimisticOpts.useExceptionProbability(getOptions())) { + if (profilingInfo != null) { + TriState exceptionSeen = profilingInfo.getExceptionSeen(bci()); + if (exceptionSeen == TriState.FALSE) { + return true; + } + } + } + } + return false; } } @@ -1754,7 +1828,7 @@ } lastInstr = intrinsicGuard.nonIntrinsicBranch; - createNonInlinedInvoke(args, bci(), targetMethod, invokeKind, resultType, returnType, null, intrinsicGuard.profile); + createNonInlinedInvoke(omitInvokeExceptionEdge(null), bci(), args, targetMethod, invokeKind, resultType, returnType, intrinsicGuard.profile); EndNode nonIntrinsicEnd = append(new EndNode()); AbstractMergeNode mergeNode = graph.add(new MergeNode()); @@ -1851,6 +1925,7 @@ if (inline(targetMethod, inlineInfo.getMethodToInline(), inlineInfo.getIntrinsicBytecodeProvider(), args)) { return SUCCESSFULLY_INLINED; } + inlineInfo = null; } /* Do not inline, and do not ask the remaining plugins. */ return inlineInfo; @@ -1922,10 +1997,10 @@ printInlining(targetMethod, inlinedMethod, false, "native method (bytecode parsing)"); return false; } - if (inlinePartialIntrinsicExit()) { + if (canInlinePartialIntrinsicExit() && InlinePartialIntrinsicExitDuringParsing.getValue(options)) { // Otherwise inline the original method. Any frame state created // during the inlining will exclude frame(s) in the - // intrinsic method (see HIRFrameStateBuilder.create(int bci)). + // intrinsic method (see FrameStateBuilder.create(int bci)). notifyBeforeInline(inlinedMethod); printInlining(targetMethod, inlinedMethod, true, "partial intrinsic exit (bytecode parsing)"); parseAndInlineCallee(intrinsic.getOriginalMethod(), args, null); @@ -1940,7 +2015,7 @@ boolean isIntrinsic = intrinsicBytecodeProvider != null; if (intrinsic == null && isIntrinsic) { assert !inlinedMethod.equals(targetMethod); - intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, intrinsicBytecodeProvider, INLINE_DURING_PARSING, args, bci()); + intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, intrinsicBytecodeProvider, INLINE_DURING_PARSING); } if (inlinedMethod.hasBytecodes()) { notifyBeforeInline(inlinedMethod); @@ -1969,9 +2044,9 @@ /** * Determines if a partial intrinsic exit (i.e., a call to the original method within an - * intrinsic) should be inlined. + * intrinsic) can be inlined. */ - protected boolean inlinePartialIntrinsicExit() { + protected boolean canInlinePartialIntrinsicExit() { return true; } @@ -2031,7 +2106,6 @@ return res; } - @SuppressWarnings("try") protected void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) { try (IntrinsicScope s = calleeIntrinsicContext != null && !parsingIntrinsic() ? new IntrinsicScope(this, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args) : null) { @@ -2049,6 +2123,9 @@ } else { ValueNode calleeReturnValue; MergeNode returnMergeNode = null; + if (s != null) { + s.returnDataList = parser.returnDataList; + } if (parser.returnDataList.size() == 1) { /* Callee has a single return, we can continue parsing at that point. */ ReturnToCallerData singleReturnData = parser.returnDataList.get(0); @@ -2091,7 +2168,6 @@ } protected InvokeWithExceptionNode createInvokeWithException(int invokeBci, CallTargetNode callTarget, JavaKind resultType) { - assert bci() == invokeBci; if (currentBlock != null && stream.nextBCI() > currentBlock.endBci) { /* * Clear non-live locals early so that the exception handler entry gets the cleared @@ -2101,7 +2177,7 @@ } AbstractBeginNode exceptionEdge = handleException(null, bci()); - InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci())); + InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, invokeBci)); frameState.pushReturn(resultType, invoke); invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke)); return invoke; @@ -3632,9 +3708,9 @@ ResolvedJavaType resolvedType = (ResolvedJavaType) type; ClassInitializationPlugin classInitializationPlugin = this.graphBuilderConfig.getPlugins().getClassInitializationPlugin(); - if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedType.getArrayClass())) { + if (classInitializationPlugin != null && classInitializationPlugin.shouldApply(this, resolvedType)) { FrameState stateBefore = frameState.create(bci(), getNonIntrinsicAncestor(), false, null, null); - classInitializationPlugin.apply(this, resolvedType.getArrayClass(), stateBefore); + classInitializationPlugin.apply(this, resolvedType, stateBefore); } for (int i = rank - 1; i >= 0; i--) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParserOptions.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParserOptions.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParserOptions.java Thu Jun 01 02:21:18 2017 +0000 @@ -40,6 +40,12 @@ @Option(help = "Inlines trivial methods during bytecode parsing.", type = OptionType.Expert) public static final OptionKey InlineDuringParsing = new OptionKey<>(true); + @Option(help = "Inlines partial intrinsic exits during bytecode parsing when possible. " + + "A partial intrinsic exit is a call within an intrinsic to the method " + + "being intrinsified and denotes semantics of the original method that " + + "the intrinsic does not support.", type = OptionType.Expert) + public static final OptionKey InlinePartialIntrinsicExitDuringParsing = new OptionKey<>(true); + @Option(help = "Inlines intrinsic methods during bytecode parsing.", type = OptionType.Expert) public static final OptionKey InlineIntrinsicsDuringParsing = new OptionKey<>(true); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java Thu Jun 01 02:21:18 2017 +0000 @@ -42,8 +42,8 @@ import org.graalvm.compiler.bytecode.Bytecode; import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode; +import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.PermanentBailoutException; -import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.StampPair; import org.graalvm.compiler.debug.Debug; @@ -307,7 +307,8 @@ public FrameState create(int bci, StateSplit forStateSplit) { if (parser != null && parser.parsingIntrinsic()) { - return parser.intrinsicContext.createFrameState(parser.getGraph(), this, forStateSplit); + NodeSourcePosition sourcePosition = createBytecodePosition(bci, false); + return parser.intrinsicContext.createFrameState(parser.getGraph(), this, forStateSplit, sourcePosition); } // Skip intrinsic frames @@ -350,8 +351,12 @@ } public NodeSourcePosition createBytecodePosition(int bci) { + return createBytecodePosition(bci, HideSubstitutionStates.getValue(parser.graph.getOptions())); + } + + private NodeSourcePosition createBytecodePosition(int bci, boolean hideSubstitutionStates) { BytecodeParser parent = parser.getParent(); - if (HideSubstitutionStates.getValue(parser.graph.getOptions())) { + if (hideSubstitutionStates) { if (parser.parsingIntrinsic()) { // Attribute to the method being replaced return new NodeSourcePosition(constantReceiver, parent.getFrameStateBuilder().createBytecodePosition(parent.bci()), parser.intrinsicContext.getOriginalMethod(), -1); @@ -359,13 +364,13 @@ // Skip intrinsic frames parent = parser.getNonIntrinsicAncestor(); } - return create(null, constantReceiver, bci, parent); + return create(null, constantReceiver, bci, parent, hideSubstitutionStates); } - private NodeSourcePosition create(NodeSourcePosition o, JavaConstant receiver, int bci, BytecodeParser parent) { + private NodeSourcePosition create(NodeSourcePosition o, JavaConstant receiver, int bci, BytecodeParser parent, boolean hideSubstitutionStates) { NodeSourcePosition outer = o; if (outer == null && parent != null) { - outer = parent.getFrameStateBuilder().createBytecodePosition(parent.bci()); + outer = parent.getFrameStateBuilder().createBytecodePosition(parent.bci(), hideSubstitutionStates); } if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) { return FrameState.toSourcePosition(outerFrameState); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArithmetic.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArithmetic.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.sparc/src/org/graalvm/compiler/lir/sparc/SPARCArithmetic.java Thu Jun 01 02:21:18 2017 +0000 @@ -170,7 +170,9 @@ assert !y.equals(scratch1); switch (opcode) { case LUREM: - crb.recordImplicitException(masm.position(), state); + if (state != null) { + crb.recordImplicitException(masm.position(), state); + } masm.udivx(asRegister(x, XWORD), crb.asIntConst(y), asRegister(scratch1, XWORD)); masm.mulx(asRegister(scratch1, XWORD), crb.asIntConst(y), asRegister(scratch2, XWORD)); getDelayedControlTransfer().emitControlTransfer(crb, masm); @@ -192,7 +194,9 @@ } assert !asRegister(xLeft, XWORD).equals(asRegister(scratch1, XWORD)); assert !asRegister(y, XWORD).equals(asRegister(scratch1, XWORD)); - crb.recordImplicitException(masm.position(), state); + if (state != null) { + crb.recordImplicitException(masm.position(), state); + } masm.udivx(asRegister(xLeft, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD)); masm.mulx(asRegister(scratch1, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD)); getDelayedControlTransfer().emitControlTransfer(crb, masm); @@ -203,7 +207,9 @@ assert !asRegister(result, WORD).equals(asRegister(scratch2, WORD)); masm.srl(asRegister(x, WORD), 0, asRegister(scratch1, WORD)); masm.srl(asRegister(y, WORD), 0, asRegister(result, WORD)); - crb.recordImplicitException(masm.position(), state); + if (state != null) { + crb.recordImplicitException(masm.position(), state); + } masm.udivx(asRegister(scratch1, WORD), asRegister(result, WORD), asRegister(scratch2, WORD)); masm.mulx(asRegister(scratch2, WORD), asRegister(result, WORD), asRegister(result, WORD)); getDelayedControlTransfer().emitControlTransfer(crb, masm); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/FixedInterval.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/FixedInterval.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/FixedInterval.java Thu Jun 01 02:21:18 2017 +0000 @@ -35,56 +35,6 @@ */ final class FixedInterval extends IntervalHint { - static final class FixedList { - - public FixedInterval fixed; - - FixedList(FixedInterval fixed) { - this.fixed = fixed; - } - - /** - * Gets the fixed list. - */ - public FixedInterval getFixed() { - return fixed; - } - - /** - * Sets the fixed list. - */ - public void setFixed(FixedInterval list) { - fixed = list; - } - - /** - * Adds an interval to a list sorted by {@linkplain FixedInterval#currentFrom() current - * from} positions. - * - * @param interval the interval to add - */ - public void addToListSortedByCurrentFromPositions(FixedInterval interval) { - FixedInterval list = getFixed(); - FixedInterval prev = null; - FixedInterval cur = list; - while (cur.currentFrom() < interval.currentFrom()) { - prev = cur; - cur = cur.next; - } - FixedInterval result = list; - if (prev == null) { - // add to head of list - result = interval; - } else { - // add before 'cur' - prev.next = interval; - } - interval.next = cur; - setFixed(result); - } - - } - /** * The fixed operand of this interval. */ diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/RegisterVerifier.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,6 +24,7 @@ import static jdk.vm.ci.code.ValueUtil.asRegister; import static jdk.vm.ci.code.ValueUtil.isRegister; +import static org.graalvm.compiler.lir.LIRValueUtil.asVariable; import static org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.isVariableOrRegister; import java.util.ArrayList; @@ -39,6 +40,7 @@ import org.graalvm.compiler.lir.LIRInstruction; import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; import org.graalvm.compiler.lir.LIRInstruction.OperandMode; +import org.graalvm.compiler.lir.Variable; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; import jdk.vm.ci.code.Register; @@ -53,7 +55,7 @@ BlockMap savedStates; // saved information of previous check // simplified access to methods of LinearScan - TraceInterval intervalAt(Value operand) { + TraceInterval intervalAt(Variable operand) { return allocator.intervalFor(operand); } @@ -189,7 +191,7 @@ Register reg = asRegister(location); int regNum = reg.number; if (interval != null) { - Debug.log("%s = %s", reg, interval.operand); + Debug.log("%s = v%d", reg, interval.operandNumber); } else if (inputState[regNum] != null) { Debug.log("%s = null", reg); } @@ -217,19 +219,19 @@ public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet flags) { // we skip spill moves inserted by the spill position optimization if (isVariableOrRegister(operand) && allocator.isProcessed(operand) && op.id() != TraceLinearScanPhase.DOMINATOR_SPILL_MOVE_ID) { - TraceInterval interval = intervalAt(operand); + TraceInterval interval = intervalAt(asVariable(operand)); if (op.id() != -1) { interval = interval.getSplitChildAtOpId(op.id(), mode); } - assert checkState(block, op, inputState, interval.operand, interval.location(), interval.splitParent()); + assert checkState(block, op, inputState, allocator.getOperand(interval), interval.location(), interval.splitParent()); } } }; InstructionValueConsumer defConsumer = (op, operand, mode, flags) -> { if (isVariableOrRegister(operand) && allocator.isProcessed(operand)) { - TraceInterval interval = intervalAt(operand); + TraceInterval interval = intervalAt(asVariable(operand)); if (op.id() != -1) { interval = interval.getSplitChildAtOpId(op.id(), mode); } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceInterval.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceInterval.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceInterval.java Thu Jun 01 02:21:18 2017 +0000 @@ -57,102 +57,6 @@ */ final class TraceInterval extends IntervalHint { - static final class AnyList { - - /** - * List of intervals whose binding is currently {@link RegisterBinding#Any}. - */ - public TraceInterval any; - - AnyList(TraceInterval any) { - this.any = any; - } - - /** - * Gets the any list. - */ - public TraceInterval getAny() { - return any; - } - - /** - * Sets the any list. - */ - public void setAny(TraceInterval list) { - any = list; - } - - /** - * Adds an interval to a list sorted by {@linkplain TraceInterval#from() current from} - * positions. - * - * @param interval the interval to add - */ - public void addToListSortedByFromPositions(TraceInterval interval) { - TraceInterval list = getAny(); - TraceInterval prev = null; - TraceInterval cur = list; - while (cur.from() < interval.from()) { - prev = cur; - cur = cur.next; - } - TraceInterval result = list; - if (prev == null) { - // add to head of list - result = interval; - } else { - // add before 'cur' - prev.next = interval; - } - interval.next = cur; - setAny(result); - } - - /** - * Adds an interval to a list sorted by {@linkplain TraceInterval#from() start} positions - * and {@linkplain TraceInterval#firstUsage(RegisterPriority) first usage} positions. - * - * @param interval the interval to add - */ - public void addToListSortedByStartAndUsePositions(TraceInterval interval) { - TraceInterval list = getAny(); - TraceInterval prev = null; - TraceInterval cur = list; - while (cur.from() < interval.from() || (cur.from() == interval.from() && cur.firstUsage(RegisterPriority.None) < interval.firstUsage(RegisterPriority.None))) { - prev = cur; - cur = cur.next; - } - if (prev == null) { - list = interval; - } else { - prev.next = interval; - } - interval.next = cur; - setAny(list); - } - - /** - * Removes an interval from a list. - * - * @param i the interval to remove - */ - public void removeAny(TraceInterval i) { - TraceInterval list = getAny(); - TraceInterval prev = null; - TraceInterval cur = list; - while (cur != i) { - assert cur != null && cur != TraceInterval.EndMarker : "interval has not been found in list: " + i; - prev = cur; - cur = cur.next; - } - if (prev == null) { - setAny(cur.next); - } else { - prev.next = cur.next; - } - } - } - /** * Constants denoting the register usage priority for an interval. The constants are declared in * increasing order of priority are are used to optimize spilling when multiple overlapping @@ -290,7 +194,7 @@ * The {@linkplain RegisterValue register} or {@linkplain Variable variable} for this interval * prior to register allocation. */ - public final AllocatableValue operand; + public final Variable operand; /** * The operand number for this interval's {@linkplain #operand operand}. @@ -375,11 +279,32 @@ private JavaConstant materializedValue; /** - * The number of times {@link #addMaterializationValue(JavaConstant)} is called. + * Sentinel interval to denote the end of an interval list. */ - private int numMaterializationValuesAdded; + static final TraceInterval EndMarker = new TraceInterval(new Variable(ValueKind.Illegal, Integer.MAX_VALUE), -1); + + TraceInterval(Variable operand) { + this(operand, operand.index); + } - private final OptionValues options; + private TraceInterval(Variable operand, int operandNumber) { + assert operand != null; + this.operand = operand; + this.operandNumber = operandNumber; + if (isRegister(operand)) { + location = operand; + } else { + assert isIllegal(operand) || isVariable(operand); + } + this.intFrom = Integer.MAX_VALUE; + this.intTo = Integer.MAX_VALUE; + this.usePosListArray = new int[4 * 2]; + this.next = EndMarker; + this.spillState = SpillState.NoDefinitionFound; + this.spillDefinitionPos = -1; + splitParent = this; + currentSplitChild = this; + } private boolean splitChildrenEmpty() { assert splitChildren == null || !splitChildren.isEmpty(); @@ -413,7 +338,7 @@ return location; } - public ValueKind kind() { + private ValueKind kind() { return operand.getValueKind(); } @@ -551,41 +476,13 @@ } /** - * Sentinel interval to denote the end of an interval list. - */ - static final TraceInterval EndMarker = new TraceInterval(Value.ILLEGAL, -1, null); - - TraceInterval(AllocatableValue operand, int operandNumber, OptionValues options) { - assert operand != null; - this.operand = operand; - this.operandNumber = operandNumber; - this.options = options; - if (isRegister(operand)) { - location = operand; - } else { - assert isIllegal(operand) || isVariable(operand); - } - this.intFrom = Integer.MAX_VALUE; - this.intTo = Integer.MAX_VALUE; - this.usePosListArray = new int[4 * 2]; - this.next = EndMarker; - this.spillState = SpillState.NoDefinitionFound; - this.spillDefinitionPos = -1; - splitParent = this; - currentSplitChild = this; - } - - /** * Sets the value which is used for re-materialization. */ public void addMaterializationValue(JavaConstant value) { - if (numMaterializationValuesAdded == 0) { - materializedValue = value; - } else { - // Interval is defined on multiple places -> no materialization is possible. - materializedValue = null; + if (materializedValue != null) { + throw GraalError.shouldNotReachHere(String.format("Multiple materialization values for %s?", this)); } - numMaterializationValuesAdded++; + materializedValue = value; } /** @@ -618,7 +515,7 @@ for (int j = i + 1; j < splitChildren.size(); j++) { TraceInterval i2 = splitChildren.get(j); - assert !i1.operand.equals(i2.operand) : "same register number"; + assert i1.operandNumber != i2.operandNumber : "same register number"; if (i1.from() < i2.from()) { assert i1.to() <= i2.from() && i1.to() < i2.to() : "intervals overlapping"; @@ -741,32 +638,6 @@ return true; } - // returns the interval that covers the given opId or null if there is none - TraceInterval getIntervalCoveringOpId(int opId) { - assert opId >= 0 : "invalid opId"; - assert opId < to() : "can only look into the past"; - - if (opId >= from()) { - return this; - } - - TraceInterval parent = splitParent(); - TraceInterval result = null; - - assert !parent.splitChildrenEmpty() : "no split children available"; - int len = parent.splitChildren.size(); - - for (int i = len - 1; i >= 0; i--) { - TraceInterval cur = parent.splitChildren.get(i); - if (cur.from() <= opId && opId < cur.to()) { - assert result == null : "covered by multiple split children " + result + " and " + cur; - result = cur; - } - } - - return result; - } - // returns the last split child that ends before the given opId TraceInterval getSplitChildBeforeOpId(int opId) { assert opId >= 0 : "invalid opId"; @@ -788,28 +659,6 @@ return result; } - // checks if opId is covered by any split child - boolean splitChildCovers(int opId, LIRInstruction.OperandMode mode) { - assert isSplitParent() : "can only be called for split parents"; - assert opId >= 0 : "invalid opId (method can not be called for spill moves)"; - - if (splitChildrenEmpty()) { - // simple case if interval was not split - return covers(opId, mode); - - } else { - // extended case: check all split children - int len = splitChildren.size(); - for (int i = 0; i < len; i++) { - TraceInterval cur = splitChildren.get(i); - if (cur.covers(opId, mode)) { - return true; - } - } - return false; - } - } - private RegisterPriority adaptPriority(RegisterPriority priority) { /* * In case of re-materialized values we require that use-operands are registers, because we @@ -824,8 +673,6 @@ // Note: use positions are sorted descending . first use has highest index int firstUsage(RegisterPriority minRegisterPriority) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - for (int i = numUsePos() - 1; i >= 0; --i) { RegisterPriority registerPriority = adaptPriority(getUsePosRegisterPriority(i)); if (registerPriority.greaterEqual(minRegisterPriority)) { @@ -836,8 +683,6 @@ } int nextUsage(RegisterPriority minRegisterPriority, int from) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - for (int i = numUsePos() - 1; i >= 0; --i) { int usePos = getUsePos(i); if (usePos >= from && adaptPriority(getUsePosRegisterPriority(i)).greaterEqual(minRegisterPriority)) { @@ -848,7 +693,6 @@ } int nextUsageExact(RegisterPriority exactRegisterPriority, int from) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; for (int i = numUsePos() - 1; i >= 0; --i) { int usePos = getUsePos(i); @@ -860,8 +704,6 @@ } int previousUsage(RegisterPriority minRegisterPriority, int from) { - assert isVariable(operand) : "cannot access use positions for fixed intervals"; - int prev = -1; for (int i = numUsePos() - 1; i >= 0; --i) { int usePos = getUsePos(i); @@ -875,11 +717,11 @@ return prev; } - public void addUsePos(int pos, RegisterPriority registerPriority) { + public void addUsePos(int pos, RegisterPriority registerPriority, OptionValues options) { assert isEmpty() || covers(pos, LIRInstruction.OperandMode.USE) : String.format("use position %d not covered by live range of interval %s", pos, this); // do not add use positions for precolored intervals because they are never used - if (registerPriority != RegisterPriority.None && isVariable(operand)) { + if (registerPriority != RegisterPriority.None) { if (DetailedAsserts.getValue(options)) { for (int i = 0; i < numUsePos(); i++) { assert pos <= getUsePos(i) : "already added a use-position with lower position"; @@ -948,7 +790,6 @@ * @return the child interval split off from this interval */ TraceInterval split(int splitPos, TraceLinearScan allocator) { - assert isVariable(operand) : "cannot split fixed intervals"; // allocate new interval TraceInterval result = newSplitChild(allocator); @@ -961,7 +802,7 @@ // split list of use positions splitUsePosAt(result, splitPos); - if (DetailedAsserts.getValue(options)) { + if (DetailedAsserts.getValue(allocator.getOptions())) { for (int i = 0; i < numUsePos(); i++) { assert getUsePos(i) < splitPos; } @@ -1037,59 +878,6 @@ return Collections.unmodifiableList(splitChildren); } - boolean isFixedInterval() { - return isRegister(operand); - } - - private static boolean isDefinitionPosition(int usePos) { - return (usePos & 1) == 1; - } - - int currentFrom(int currentPosition) { - assert isFixedInterval(); - for (int i = 0; i < numUsePos(); i++) { - int usePos = getUsePos(i); - if (usePos <= currentPosition && isDefinitionPosition(usePos)) { - return usePos; - } - - } - return Integer.MAX_VALUE; - } - - int currentIntersectsAt(int currentPosition, TraceInterval current) { - assert isFixedInterval(); - assert !current.isFixedInterval(); - int from = Integer.MAX_VALUE; - int to = Integer.MIN_VALUE; - - for (int i = 0; i < numUsePos(); i++) { - int usePos = getUsePos(i); - if (isDefinitionPosition(usePos)) { - if (usePos <= currentPosition) { - from = usePos; - break; - } - to = Integer.MIN_VALUE; - } else { - if (to < usePos) { - to = usePos; - } - } - } - if (from < current.from()) { - if (to <= current.from()) { - return -1; - } - return current.from(); - } else { - if (current.to() <= from) { - return -1; - } - return from; - } - } - /* * UsePos * diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceIntervalWalker.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceIntervalWalker.java Wed May 31 09:19:28 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.graalvm.compiler.lir.alloc.trace.lsra; - -import org.graalvm.compiler.debug.Debug; -import org.graalvm.compiler.debug.Indent; -import org.graalvm.compiler.lir.alloc.trace.lsra.FixedInterval.FixedList; -import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.AnyList; -import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterBinding; -import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.State; -import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; - -/** - */ -class TraceIntervalWalker { - - protected final TraceLinearScan allocator; - - /** - * Sorted list of intervals, not live before the current position. - */ - protected AnyList unhandledAnyList; - - /** - * Sorted list of intervals, live at the current position. - */ - protected AnyList activeAnyList; - protected FixedList activeFixedList; - - /** - * Sorted list of intervals in a life time hole at the current position. - */ - protected FixedList inactiveFixedList; - - /** - * The current position (intercept point through the intervals). - */ - protected int currentPosition; - - /** - * Processes the {@code currentInterval} interval in an attempt to allocate a physical register - * to it and thus allow it to be moved to a list of {@linkplain #activeAnyList active} - * intervals. - * - * @param currentInterval The interval to be activated. - * - * @return {@code true} if a register was allocated to the {@code currentInterval} interval - */ - protected boolean activateCurrent(TraceInterval currentInterval) { - if (Debug.isLogEnabled()) { - logCurrentStatus(); - } - return true; - } - - @SuppressWarnings("try") - protected void logCurrentStatus() { - try (Indent i = Debug.logAndIndent("active:")) { - logList(activeFixedList.getFixed()); - logList(activeAnyList.getAny()); - } - try (Indent i = Debug.logAndIndent("inactive(fixed):")) { - logList(inactiveFixedList.getFixed()); - } - } - - private static void logList(FixedInterval i) { - for (FixedInterval interval = i; interval != FixedInterval.EndMarker; interval = interval.next) { - Debug.log("%s", interval.logString()); - } - } - - private static void logList(TraceInterval i) { - for (TraceInterval interval = i; interval != TraceInterval.EndMarker; interval = interval.next) { - Debug.log("%s", interval.logString()); - } - } - - void walkBefore(int lirOpId) { - walkTo(lirOpId - 1); - } - - void walk() { - walkTo(Integer.MAX_VALUE); - } - - /** - * Creates a new interval walker. - * - * @param allocator the register allocator context - * @param unhandledFixed the list of unhandled {@linkplain RegisterBinding#Fixed fixed} - * intervals - * @param unhandledAny the list of unhandled {@linkplain RegisterBinding#Any non-fixed} - * intervals - */ - TraceIntervalWalker(TraceLinearScan allocator, FixedInterval unhandledFixed, TraceInterval unhandledAny) { - this.allocator = allocator; - - unhandledAnyList = new AnyList(unhandledAny); - activeAnyList = new AnyList(TraceInterval.EndMarker); - activeFixedList = new FixedList(FixedInterval.EndMarker); - // we don't need a separate unhandled list for fixed. - inactiveFixedList = new FixedList(unhandledFixed); - currentPosition = -1; - } - - protected void removeFromList(TraceInterval interval) { - activeAnyList.removeAny(interval); - } - - /** - * Walks up to {@code from} and updates the state of {@link FixedInterval fixed intervals}. - * - * Fixed intervals can switch back and forth between the states {@link State#Active} and - * {@link State#Inactive} (and eventually to {@link State#Handled} but handled intervals are not - * managed). - */ - @SuppressWarnings("try") - private void walkToFixed(State state, int from) { - assert state == State.Active || state == State.Inactive : "wrong state"; - FixedInterval prevprev = null; - FixedInterval prev = (state == State.Active) ? activeFixedList.getFixed() : inactiveFixedList.getFixed(); - FixedInterval next = prev; - if (Debug.isLogEnabled()) { - try (Indent i = Debug.logAndIndent("walkToFixed(%s, %d):", state, from)) { - logList(next); - } - } - while (next.currentFrom() <= from) { - FixedInterval cur = next; - next = cur.next; - - boolean rangeHasChanged = false; - while (cur.currentTo() <= from) { - cur.nextRange(); - rangeHasChanged = true; - } - - // also handle move from inactive list to active list - rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from); - - if (rangeHasChanged) { - // remove cur from list - if (prevprev == null) { - if (state == State.Active) { - activeFixedList.setFixed(next); - } else { - inactiveFixedList.setFixed(next); - } - } else { - prevprev.next = next; - } - prev = next; - TraceInterval.State newState; - if (cur.currentAtEnd()) { - // move to handled state (not maintained as a list) - newState = State.Handled; - } else { - if (cur.currentFrom() <= from) { - // sort into active list - activeFixedList.addToListSortedByCurrentFromPositions(cur); - newState = State.Active; - } else { - // sort into inactive list - inactiveFixedList.addToListSortedByCurrentFromPositions(cur); - newState = State.Inactive; - } - if (prev == cur) { - assert state == newState; - prevprev = prev; - prev = cur.next; - } - } - intervalMoved(cur, state, newState); - } else { - prevprev = prev; - prev = cur.next; - } - } - } - - /** - * Walks up to {@code from} and updates the state of {@link TraceInterval intervals}. - * - * Trace intervals can switch once from {@link State#Unhandled} to {@link State#Active} and then - * to {@link State#Handled} but handled intervals are not managed. - */ - @SuppressWarnings("try") - private void walkToAny(int from) { - TraceInterval prevprev = null; - TraceInterval prev = activeAnyList.getAny(); - TraceInterval next = prev; - if (Debug.isLogEnabled()) { - try (Indent i = Debug.logAndIndent("walkToAny(%d):", from)) { - logList(next); - } - } - while (next.from() <= from) { - TraceInterval cur = next; - next = cur.next; - - if (cur.to() <= from) { - // remove cur from list - if (prevprev == null) { - activeAnyList.setAny(next); - } else { - prevprev.next = next; - } - intervalMoved(cur, State.Active, State.Handled); - } else { - prevprev = prev; - } - prev = next; - } - } - - /** - * Get the next interval from {@linkplain #unhandledAnyList} which starts before or at - * {@code toOpId}. The returned interval is removed. - * - * @postcondition all intervals in {@linkplain #unhandledAnyList} start after {@code toOpId}. - * - * @return The next interval or null if there is no {@linkplain #unhandledAnyList unhandled} - * interval at position {@code toOpId}. - */ - private TraceInterval nextInterval(int toOpId) { - TraceInterval any = unhandledAnyList.getAny(); - - if (any != TraceInterval.EndMarker) { - TraceInterval currentInterval = unhandledAnyList.getAny(); - if (toOpId < currentInterval.from()) { - return null; - } - - unhandledAnyList.setAny(currentInterval.next); - currentInterval.next = TraceInterval.EndMarker; - return currentInterval; - } - return null; - - } - - /** - * Walk up to {@code toOpId}. - * - * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeFixedList} - * and {@link #inactiveFixedList} are populated. - */ - @SuppressWarnings("try") - protected void walkTo(int toOpId) { - assert currentPosition <= toOpId : "can not walk backwards"; - for (TraceInterval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) { - int opId = currentInterval.from(); - - // set currentPosition prior to call of walkTo - currentPosition = opId; - - // update unhandled stack intervals - // updateUnhandledStackIntervals(opId); - - // call walkTo even if currentPosition == id - walkToFixed(State.Active, opId); - walkToFixed(State.Inactive, opId); - walkToAny(opId); - - try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) { - if (activateCurrent(currentInterval)) { - activeAnyList.addToListSortedByFromPositions(currentInterval); - intervalMoved(currentInterval, State.Unhandled, State.Active); - } - } - } - // set currentPosition prior to call of walkTo - currentPosition = toOpId; - - if (currentPosition <= allocator.maxOpId()) { - // update unhandled stack intervals - // updateUnhandledStackIntervals(toOpId); - - // call walkTo if still in range - walkToFixed(State.Active, toOpId); - walkToFixed(State.Inactive, toOpId); - walkToAny(toOpId); - } - } - - private static void intervalMoved(IntervalHint interval, State from, State to) { - // intervalMoved() is called whenever an interval moves from one interval list to another. - // In the implementation of this method it is prohibited to move the interval to any list. - if (Debug.isLogEnabled()) { - Debug.log("interval moved from %s to %s: %s", from, to, interval.logString()); - } - } -} diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanAssignLocationsPhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,6 +24,7 @@ import static jdk.vm.ci.code.ValueUtil.isIllegal; import static jdk.vm.ci.code.ValueUtil.isRegister; +import static org.graalvm.compiler.lir.LIRValueUtil.asVariable; import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue; import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; import static org.graalvm.compiler.lir.LIRValueUtil.isVariable; @@ -108,7 +109,7 @@ return getLocation(op, interval, mode); } - private static Value getLocation(LIRInstruction op, TraceInterval interval, OperandMode mode) { + private Value getLocation(LIRInstruction op, TraceInterval interval, OperandMode mode) { if (isIllegal(interval.location()) && interval.canMaterialize()) { if (op instanceof LabelOp) { /* @@ -118,7 +119,7 @@ return Value.ILLEGAL; } assert mode != OperandMode.DEF; - return new ConstantValue(interval.kind(), interval.getMaterializedValue()); + return new ConstantValue(allocator.getKind(interval), interval.getMaterializedValue()); } return interval.location(); } @@ -226,7 +227,7 @@ return values; } - private static Value valueAtBlockBoundary(LIRInstruction instruction, TraceInterval interval, OperandMode mode) { + private Value valueAtBlockBoundary(LIRInstruction instruction, TraceInterval interval, OperandMode mode) { if (mode == OperandMode.DEF && interval == null) { // not needed in this trace return Value.ILLEGAL; @@ -275,7 +276,7 @@ // remove useless moves if (MoveOp.isMoveOp(op)) { AllocatableValue result = MoveOp.asMoveOp(op).getResult(); - if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) { + if (isVariable(result) && allocator.isMaterialized(asVariable(result), op.id(), OperandMode.DEF)) { /* * This happens if a materializable interval is originally not spilled but then * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,6 +24,7 @@ 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; @@ -121,11 +122,10 @@ if (Debug.isLogEnabled()) { if (ValueMoveOp.isValueMoveOp(op)) { ValueMoveOp vmove = ValueMoveOp.asValueMoveOp(op); - Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(vmove.getInput()), vmove.getInput(), - allocator.operandNumber(vmove.getResult()), vmove.getResult(), block); + Debug.log("eliminating move from interval %s to %s in block %s", vmove.getInput(), vmove.getResult(), block); } else { LoadConstantOp load = LoadConstantOp.asLoadConstantOp(op); - Debug.log("eliminating constant load from %s to %d (%s) in block %s", load.getConstant(), allocator.operandNumber(load.getResult()), load.getResult(), + Debug.log("eliminating constant load from %s to %s in block %s", load.getConstant(), load.getResult(), block); } } @@ -200,7 +200,7 @@ assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move; assert lastOpId >= 0 : "Invalid lastOpId: " + lastOpId; - TraceInterval curInterval = allocator.intervalFor(move.getResult()); + TraceInterval curInterval = allocator.intervalFor(asVariable(move.getResult())); if (!isRegister(curInterval.location()) && curInterval.inMemoryAt(lastOpId) && !isPhiResolutionMove(allocator, move)) { /* Phi resolution moves cannot be removed because they define the value. */ @@ -221,7 +221,7 @@ */ private static boolean isPhiResolutionMove(TraceLinearScan allocator, MoveOp move) { assert ((LIRInstruction) move).id() == -1 : "Not a spill move: " + move; - TraceInterval curInterval = allocator.intervalFor(move.getResult()); + TraceInterval curInterval = allocator.intervalFor(asVariable(move.getResult())); return curInterval.isSplitParent(); } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanLifetimeAnalysisPhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -191,11 +191,11 @@ }; private void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority) { - if (!allocator.isProcessed(operand)) { - return; - } if (isRegister(operand)) { - addFixedUse(asRegisterValue(operand), from, to); + RegisterValue reg = asRegisterValue(operand); + if (allocator.isAllocatable(reg)) { + addFixedUse(reg, from, to); + } } else { assert isVariable(operand) : operand; addVariableUse(asVariable(operand), from, to, registerPriority); @@ -215,7 +215,7 @@ interval.addRange(from, to); // Register use position at even instruction id. - interval.addUsePos(to & ~1, registerPriority); + interval.addUsePos(to & ~1, registerPriority, allocator.getOptions()); if (Debug.isLogEnabled()) { Debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name()); @@ -223,11 +223,11 @@ } private void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority) { - if (!allocator.isProcessed(operand)) { - return; - } if (isRegister(operand)) { - addFixedDef(asRegisterValue(operand), op); + RegisterValue reg = asRegisterValue(operand); + if (allocator.isAllocatable(reg)) { + addFixedDef(reg, op); + } } else { assert isVariable(operand) : operand; addVariableDef(asVariable(operand), op, registerPriority); @@ -280,7 +280,7 @@ } if (!(op instanceof LabelOp)) { // no use positions for labels - interval.addUsePos(defPos, registerPriority); + interval.addUsePos(defPos, registerPriority, allocator.getOptions()); } changeSpillDefinitionPos(op, operand, interval, defPos); @@ -297,11 +297,11 @@ } private void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority) { - if (!allocator.isProcessed(operand)) { - return; - } if (isRegister(operand)) { - addFixedTemp(asRegisterValue(operand), tempPos); + RegisterValue reg = asRegisterValue(operand); + if (allocator.isAllocatable(reg)) { + addFixedTemp(reg, tempPos); + } } else { assert isVariable(operand) : operand; addVariableTemp(asVariable(operand), tempPos, registerPriority); @@ -325,7 +325,7 @@ interval.setFrom(tempPos); } - interval.addUsePos(tempPos, registerPriority); + interval.addUsePos(tempPos, registerPriority, allocator.getOptions()); interval.addMaterializationValue(null); if (Debug.isLogEnabled()) { @@ -404,9 +404,9 @@ return null; } from = getIntervalHint(toValue); - to = allocator.getOrCreateInterval(fromValue); + to = allocator.getOrCreateInterval(asVariable(fromValue)); } else { - to = allocator.getOrCreateInterval(toValue); + to = allocator.getOrCreateInterval(asVariable(toValue)); from = getIntervalHint(fromValue); } @@ -699,7 +699,7 @@ if (isRegister(from)) { return allocator.getOrCreateFixedInterval(asRegisterValue(from)); } - return allocator.getOrCreateInterval(from); + return allocator.getOrCreateInterval(asVariable(from)); } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -24,7 +24,6 @@ import static jdk.vm.ci.code.CodeUtil.isEven; import static jdk.vm.ci.code.ValueUtil.asRegister; -import static jdk.vm.ci.code.ValueUtil.asRegisterValue; import static jdk.vm.ci.code.ValueUtil.isIllegal; import static jdk.vm.ci.code.ValueUtil.isLegal; import static jdk.vm.ci.code.ValueUtil.isRegister; @@ -33,7 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.EnumSet; +import java.util.Comparator; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig; @@ -46,11 +45,8 @@ import org.graalvm.compiler.debug.Indent; import org.graalvm.compiler.lir.LIR; import org.graalvm.compiler.lir.LIRInstruction; -import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; import org.graalvm.compiler.lir.LIRInstruction.OperandMode; -import org.graalvm.compiler.lir.LIRValueUtil; import org.graalvm.compiler.lir.StandardOp.BlockEndOp; -import org.graalvm.compiler.lir.ValueConsumer; import org.graalvm.compiler.lir.Variable; import org.graalvm.compiler.lir.VirtualStackSlot; import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessInfo; @@ -149,27 +145,26 @@ abstract boolean apply(TraceInterval i); } - static final IntervalPredicate IS_PRECOLORED_INTERVAL = new IntervalPredicate() { - - @Override - public boolean apply(TraceInterval i) { - return isRegister(i.operand); - } - }; - static final IntervalPredicate IS_VARIABLE_INTERVAL = new IntervalPredicate() { @Override public boolean apply(TraceInterval i) { - return isVariable(i.operand); + // all TraceIntervals are variable intervals + return true; } }; - - static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() { + private static final Comparator SORT_BY_FROM_COMP = new Comparator() { @Override - public boolean apply(TraceInterval i) { - return !isRegister(i.operand); + public int compare(TraceInterval a, TraceInterval b) { + return a.from() - b.from(); + } + }; + private static final Comparator SORT_BY_SPILL_POS_COMP = new Comparator() { + + @Override + public int compare(TraceInterval a, TraceInterval b) { + return a.spillDefinitionPos() - b.spillDefinitionPos(); } }; @@ -185,7 +180,9 @@ private static boolean isSortedByFrom(T[] intervals) { int from = -1; for (T interval : intervals) { - assert interval != null; + if (interval == null) { + continue; + } assert from <= interval.from(); from = interval.from(); } @@ -202,13 +199,13 @@ return true; } - private static T[] sortIntervalsBeforeAllocation(T[] intervals, T[] sortedList) { + private static TraceInterval[] sortIntervalsBeforeAllocation(TraceInterval[] intervals, TraceInterval[] sortedList) { int sortedIdx = 0; int sortedFromMax = -1; // special sorting algorithm: the original interval-list is almost sorted, // only some intervals are swapped. So this is much faster than a complete QuickSort - for (T interval : intervals) { + for (TraceInterval interval : intervals) { if (interval != null) { int from = interval.from(); @@ -237,11 +234,6 @@ */ private TraceInterval[] sortedIntervals; - /** - * Fixed intervals sorted by {@link FixedInterval#from()}. - */ - private FixedInterval[] sortedFixedIntervals; - private final Trace trace; public TraceLinearScan(Trace trace) { @@ -254,14 +246,10 @@ } /** - * Converts an operand (variable or register) to an index in a flat address space covering - * all the {@linkplain Variable variables} and {@linkplain RegisterValue registers} being - * processed by this allocator. + * @return {@link Variable#index} */ - int operandNumber(Value operand) { - assert !isRegister(operand) : "Register do not have operand numbers: " + operand; - assert isVariable(operand) : "Unsupported Value " + operand; - return ((Variable) operand).index; + int operandNumber(Variable operand) { + return operand.index; } OptionValues getOptions() { @@ -303,6 +291,10 @@ return registerAttributes[reg.number]; } + public boolean isAllocatable(RegisterValue register) { + return attributes(register.getRegister()).isAllocatable(); + } + public MoveFactory getSpillMoveFactory() { return moveFactory; } @@ -331,20 +323,20 @@ /** * Returns a new spill slot or a cached entry if there is already one for the - * {@linkplain TraceInterval#operand variable}. + * {@linkplain TraceInterval variable}. */ private AllocatableValue allocateSpillSlot(TraceInterval interval) { - int variableIndex = LIRValueUtil.asVariable(interval.splitParent().operand).index; + int variableIndex = interval.splitParent().operandNumber; OptionValues options = getOptions(); if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue(options)) { AllocatableValue cachedStackSlot = cachedStackSlots[variableIndex]; if (cachedStackSlot != null) { TraceRegisterAllocationPhase.globalStackSlots.increment(); - assert cachedStackSlot.getValueKind().equals(interval.kind()) : "CachedStackSlot: kind mismatch? " + interval.kind() + " vs. " + cachedStackSlot.getValueKind(); + assert cachedStackSlot.getValueKind().equals(getKind(interval)) : "CachedStackSlot: kind mismatch? " + getKind(interval) + " vs. " + cachedStackSlot.getValueKind(); return cachedStackSlot; } } - VirtualStackSlot slot = frameMapBuilder.allocateSpillSlot(interval.kind()); + VirtualStackSlot slot = frameMapBuilder.allocateSpillSlot(getKind(interval)); if (TraceRegisterAllocationPhase.Options.TraceRACacheStackSlots.getValue(options)) { cachedStackSlots[variableIndex] = slot; } @@ -457,16 +449,13 @@ } FixedInterval createFixedUnhandledList() { - assert isSortedByFrom(sortedFixedIntervals) : "interval list is not sorted"; + assert isSortedByFrom(fixedIntervals) : "interval list is not sorted"; FixedInterval list1 = FixedInterval.EndMarker; FixedInterval list1Prev = null; - FixedInterval v; + for (FixedInterval v : fixedIntervals) { - int n = sortedFixedIntervals.length; - for (int i = 0; i < n; i++) { - v = sortedFixedIntervals[i]; if (v == null) { continue; } @@ -497,16 +486,6 @@ sortedIntervals = TraceLinearScanPhase.sortIntervalsBeforeAllocation(intervals(), new TraceInterval[sortedLen]); } - protected void sortFixedIntervalsBeforeAllocation() { - int sortedLen = 0; - for (FixedInterval interval : fixedIntervals()) { - if (interval != null) { - sortedLen++; - } - } - sortedFixedIntervals = TraceLinearScanPhase.sortIntervalsBeforeAllocation(fixedIntervals(), new FixedInterval[sortedLen]); - } - void sortIntervalsAfterAllocation() { if (hasDerivedIntervals()) { // no intervals have been added during allocation, so sorted list is already up to @@ -520,7 +499,7 @@ int newLen = newList.length; // conventional sort-algorithm for new intervals - Arrays.sort(newList, (TraceInterval a, TraceInterval b) -> a.from() - b.from()); + Arrays.sort(newList, SORT_BY_FROM_COMP); // merge old and new list (both already sorted) into one combined list TraceInterval[] combinedList = new TraceInterval[oldLen + newLen]; @@ -543,7 +522,7 @@ void sortIntervalsBySpillPos() { // TODO (JE): better algorithm? // conventional sort-algorithm for new intervals - Arrays.sort(sortedIntervals, (TraceInterval a, TraceInterval b) -> a.spillDefinitionPos() - b.spillDefinitionPos()); + Arrays.sort(sortedIntervals, SORT_BY_SPILL_POS_COMP); } // wrapper for Interval.splitChildAtOpId that performs a bailout in product mode @@ -565,7 +544,7 @@ return interval.spillSlot(); } - boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) { + boolean isMaterialized(Variable operand, int opId, OperandMode mode) { TraceInterval interval = intervalFor(operand); assert interval != null : "interval must exist"; @@ -600,7 +579,6 @@ printIntervals("Before register allocation"); sortIntervalsBeforeAllocation(); - sortFixedIntervalsBeforeAllocation(); TRACE_LINEAR_SCAN_REGISTER_ALLOCATION_PHASE.apply(target, lirGenRes, trace, spillMoveFactory, registerAllocationConfig, traceBuilderResult, this, false); printIntervals("After register allocation"); @@ -671,7 +649,7 @@ throw new GraalError(""); } - if (isVariable(i1.operand) && i1.kind().equals(LIRKind.Illegal)) { + if (getKind(i1).equals(LIRKind.Illegal)) { Debug.log("Interval %d has no type assigned", i1.operandNumber); Debug.log(i1.logString()); throw new GraalError(""); @@ -736,74 +714,6 @@ } } - class CheckConsumer implements ValueConsumer { - - boolean ok; - FixedInterval curInterval; - - @Override - public void visitValue(Value operand, OperandMode mode, EnumSet flags) { - if (isRegister(operand)) { - if (fixedIntervalFor(asRegisterValue(operand)) == curInterval) { - ok = true; - } - } - } - } - - @SuppressWarnings("try") - void verifyNoOopsInFixedIntervals() { - try (Indent indent = Debug.logAndIndent("verifying that no oops are in fixed intervals *")) { - CheckConsumer checkConsumer = new CheckConsumer(); - - TraceInterval otherIntervals; - FixedInterval fixedInts = createFixedUnhandledList(); - // to ensure a walking until the last instruction id, add a dummy interval - // with a high operation id - otherIntervals = new TraceInterval(Value.ILLEGAL, -1, getOptions()); - otherIntervals.addRange(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1); - TraceIntervalWalker iw = new TraceIntervalWalker(this, fixedInts, otherIntervals); - - for (AbstractBlockBase block : sortedBlocks()) { - ArrayList instructions = getLIR().getLIRforBlock(block); - - for (int j = 0; j < instructions.size(); j++) { - LIRInstruction op = instructions.get(j); - - if (op.hasState()) { - iw.walkBefore(op.id()); - boolean checkLive = true; - - /* - * Make sure none of the fixed registers is live across an oopmap since - * we can't handle that correctly. - */ - if (checkLive) { - for (FixedInterval interval = iw.activeFixedList.getFixed(); interval != FixedInterval.EndMarker; interval = interval.next) { - if (interval.to() > op.id() + 1) { - /* - * This interval is live out of this op so make sure that - * this interval represents some value that's referenced by - * this op either as an input or output. - */ - checkConsumer.curInterval = interval; - checkConsumer.ok = false; - - op.visitEachInput(checkConsumer); - op.visitEachAlive(checkConsumer); - op.visitEachTemp(checkConsumer); - op.visitEachOutput(checkConsumer); - - assert checkConsumer.ok : "fixed intervals should never be live across an oopmap point"; - } - } - } - } - } - } - } - } - public LIR getLIR() { return res.getLIR(); } @@ -872,14 +782,14 @@ private AbstractBlockBase[] opIdToBlockMap; /** - * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + * Map from {@linkplain #operandNumber operand numbers} to intervals. */ TraceInterval[] intervals() { return intervals; } /** - * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals. + * Map from {@linkplain Register#number} to fixed intervals. */ FixedInterval[] fixedIntervals() { return fixedIntervals; @@ -910,10 +820,11 @@ * @param operand the operand for the interval * @return the created interval */ - private TraceInterval createInterval(AllocatableValue operand) { + private TraceInterval createInterval(Variable operand) { assert isLegal(operand); int operandNumber = operandNumber(operand); - TraceInterval interval = new TraceInterval(operand, operandNumber, getOptions()); + assert operand.index == operandNumber; + TraceInterval interval = new TraceInterval(operand); assert operandNumber < intervalsSize; assert intervals[operandNumber] == null; intervals[operandNumber] = interval; @@ -934,7 +845,7 @@ intervals = Arrays.copyOf(intervals, intervals.length + (intervals.length >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT) + 1); } // increments intervalsSize - Variable variable = createVariable(source.kind()); + Variable variable = createVariable(getKind(source)); assert intervalsSize <= intervals.length; @@ -976,7 +887,7 @@ } } - TraceInterval intervalFor(Value operand) { + TraceInterval intervalFor(Variable operand) { return intervalFor(operandNumber(operand)); } @@ -985,7 +896,7 @@ return intervals[operandNumber]; } - TraceInterval getOrCreateInterval(AllocatableValue operand) { + TraceInterval getOrCreateInterval(Variable operand) { TraceInterval ret = intervalFor(operand); if (ret == null) { return createInterval(operand); @@ -1095,6 +1006,34 @@ return TraceUtil.hasInterTraceSuccessor(traceBuilderResult, trace, block); } + private void printInterval(TraceInterval interval, IntervalVisitor visitor) { + Value hint = interval.locationHint(false) != null ? interval.locationHint(false).location() : null; + AllocatableValue operand = getOperand(interval); + String type = getKind(interval).getPlatformKind().toString(); + visitor.visitIntervalStart(getOperand(interval.splitParent()), operand, interval.location(), hint, type); + + // print ranges + visitor.visitRange(interval.from(), interval.to()); + + // print use positions + int prev = -1; + for (int i = interval.numUsePos() - 1; i >= 0; --i) { + assert prev < interval.getUsePos(i) : "use positions not sorted"; + visitor.visitUsePos(interval.getUsePos(i), interval.getUsePosRegisterPriority(i)); + prev = interval.getUsePos(i); + } + + visitor.visitIntervalEnd(interval.spillState()); + } + + AllocatableValue getOperand(TraceInterval interval) { + return interval.operand; + } + + ValueKind getKind(TraceInterval interval) { + return getOperand(interval).getValueKind(); + } + } public static boolean verifyEquals(TraceLinearScan a, TraceLinearScan b) { @@ -1124,7 +1063,7 @@ return; } assert b != null : "Second interval is null but forst is: " + a; - assert a.operand.equals(b.operand) : "Operand mismatch: " + a + " vs. " + b; + assert a.operandNumber == b.operandNumber : "Operand mismatch: " + a + " vs. " + b; assert a.from() == b.from() : "From mismatch: " + a + " vs. " + b; assert a.to() == b.to() : "To mismatch: " + a + " vs. " + b; assert verifyIntervalsEquals(a, b); @@ -1209,23 +1148,4 @@ } - private static void printInterval(TraceInterval interval, IntervalVisitor visitor) { - Value hint = interval.locationHint(false) != null ? interval.locationHint(false).location() : null; - AllocatableValue operand = interval.operand; - String type = isRegister(operand) ? "fixed" : operand.getValueKind().getPlatformKind().toString(); - visitor.visitIntervalStart(interval.splitParent().operand, operand, interval.location(), hint, type); - - // print ranges - visitor.visitRange(interval.from(), interval.to()); - - // print use positions - int prev = -1; - for (int i = interval.numUsePos() - 1; i >= 0; --i) { - assert prev < interval.getUsePos(i) : "use positions not sorted"; - visitor.visitUsePos(interval.getUsePos(i), interval.getUsePosRegisterPriority(i)); - prev = interval.getUsePos(i); - } - - visitor.visitIntervalEnd(interval.spillState()); - } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -25,6 +25,7 @@ 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; import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot; @@ -190,13 +191,13 @@ // no need to handle virtual stack slots return; } - TraceInterval toParent = allocator.intervalFor(phiTo); + TraceInterval toParent = allocator.intervalFor(asVariable(phiTo)); if (isConstantValue(phiFrom)) { numResolutionMoves.increment(); TraceInterval toInterval = allocator.splitChildAtOpId(toParent, toId, LIRInstruction.OperandMode.DEF); moveResolver.addMapping(asConstant(phiFrom), toInterval); } else { - addMapping(allocator.intervalFor(phiFrom), toParent, fromId, toId, moveResolver); + addMapping(allocator.intervalFor(asVariable(phiFrom)), toParent, fromId, toId, moveResolver); } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanWalker.java Thu Jun 01 02:21:18 2017 +0000 @@ -39,12 +39,14 @@ import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.Indent; import org.graalvm.compiler.lir.LIRInstruction; +import org.graalvm.compiler.lir.LIRValueUtil; import org.graalvm.compiler.lir.StandardOp.BlockEndOp; import org.graalvm.compiler.lir.StandardOp.LabelOp; import org.graalvm.compiler.lir.StandardOp.ValueMoveOp; import org.graalvm.compiler.lir.alloc.OutOfRegistersException; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterPriority; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.SpillState; +import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.State; import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan; import org.graalvm.compiler.lir.ssa.SSAUtil; @@ -53,7 +55,110 @@ /** */ -final class TraceLinearScanWalker extends TraceIntervalWalker { +final class TraceLinearScanWalker { + + /** + * Adds an interval to a list sorted by {@linkplain FixedInterval#currentFrom() current from} + * positions. + * + * @param list the list + * @param interval the interval to add + * @return the new head of the list + */ + private static FixedInterval addToListSortedByCurrentFromPositions(FixedInterval list, FixedInterval interval) { + FixedInterval prev = null; + FixedInterval cur = list; + while (cur.currentFrom() < interval.currentFrom()) { + prev = cur; + cur = cur.next; + } + FixedInterval result = list; + if (prev == null) { + // add to head of list + result = interval; + } else { + // add before 'cur' + prev.next = interval; + } + interval.next = cur; + return result; + } + + /** + * Adds an interval to a list sorted by {@linkplain TraceInterval#from() current from} + * positions. + * + * @param list the list + * @param interval the interval to add + * @return the new head of the list + */ + private static TraceInterval addToListSortedByFromPositions(TraceInterval list, TraceInterval interval) { + TraceInterval prev = null; + TraceInterval cur = list; + while (cur.from() < interval.from()) { + prev = cur; + cur = cur.next; + } + TraceInterval result = list; + if (prev == null) { + // add to head of list + result = interval; + } else { + // add before 'cur' + prev.next = interval; + } + interval.next = cur; + return result; + } + + /** + * Adds an interval to a list sorted by {@linkplain TraceInterval#from() start} positions and + * {@linkplain TraceInterval#firstUsage(RegisterPriority) first usage} positions. + * + * @param list the list + * @param interval the interval to add + * @return the new head of the list + */ + private static TraceInterval addToListSortedByStartAndUsePositions(TraceInterval list, TraceInterval interval) { + TraceInterval newHead = list; + TraceInterval prev = null; + TraceInterval cur = newHead; + while (cur.from() < interval.from() || (cur.from() == interval.from() && cur.firstUsage(RegisterPriority.None) < interval.firstUsage(RegisterPriority.None))) { + prev = cur; + cur = cur.next; + } + if (prev == null) { + newHead = interval; + } else { + prev.next = interval; + } + interval.next = cur; + return newHead; + } + + /** + * Removes an interval from a list. + * + * @param list the list + * @param interval the interval to remove + * @return the new head of the list + */ + private static TraceInterval removeAny(TraceInterval list, TraceInterval interval) { + TraceInterval newHead = list; + TraceInterval prev = null; + TraceInterval cur = newHead; + while (cur != interval) { + assert cur != null && cur != TraceInterval.EndMarker : "interval has not been found in list: " + interval; + prev = cur; + cur = cur.next; + } + if (prev == null) { + newHead = cur.next; + } else { + prev.next = cur.next; + } + return newHead; + } private Register[] availableRegs; @@ -69,6 +174,30 @@ private int maxReg; + private final TraceLinearScan allocator; + + /** + * Sorted list of intervals, not live before the current position. + */ + private TraceInterval unhandledAnyList; + + /** + * Sorted list of intervals, live at the current position. + */ + private TraceInterval activeAnyList; + + private FixedInterval activeFixedList; + + /** + * Sorted list of intervals in a life time hole at the current position. + */ + private FixedInterval inactiveFixedList; + + /** + * The current position (intercept point through the intervals). + */ + private int currentPosition; + /** * Only 10% of the lists in {@link #spillIntervals} are actually used. But when they are used, * they can grow quite long. The maximum length observed was 45 (all numbers taken from a @@ -92,7 +221,14 @@ } TraceLinearScanWalker(TraceLinearScan allocator, FixedInterval unhandledFixedFirst, TraceInterval unhandledAnyFirst) { - super(allocator, unhandledFixedFirst, unhandledAnyFirst); + this.allocator = allocator; + + unhandledAnyList = unhandledAnyFirst; + activeAnyList = TraceInterval.EndMarker; + activeFixedList = FixedInterval.EndMarker; + // we don't need a separate unhandled list for fixed. + inactiveFixedList = unhandledFixedFirst; + currentPosition = -1; moveResolver = allocator.createMoveResolver(); int numRegs = allocator.getRegisters().size(); @@ -190,7 +326,7 @@ } private void freeExcludeActiveFixed() { - FixedInterval interval = activeFixedList.getFixed(); + FixedInterval interval = activeFixedList; while (interval != FixedInterval.EndMarker) { assert isRegister(interval.location()) : "active interval must have a register assigned"; excludeFromUse(interval); @@ -199,7 +335,7 @@ } private void freeExcludeActiveAny() { - TraceInterval interval = activeAnyList.getAny(); + TraceInterval interval = activeAnyList; while (interval != TraceInterval.EndMarker) { assert isRegister(interval.location()) : "active interval must have a register assigned"; excludeFromUse(interval); @@ -208,7 +344,7 @@ } private void freeCollectInactiveFixed(TraceInterval current) { - FixedInterval interval = inactiveFixedList.getFixed(); + FixedInterval interval = inactiveFixedList; while (interval != FixedInterval.EndMarker) { if (current.to() <= interval.from()) { assert interval.intersectsAt(current) == -1 : "must not intersect"; @@ -221,7 +357,7 @@ } private void spillExcludeActiveFixed() { - FixedInterval interval = activeFixedList.getFixed(); + FixedInterval interval = activeFixedList; while (interval != FixedInterval.EndMarker) { excludeFromUse(interval); interval = interval.next; @@ -229,7 +365,7 @@ } private void spillBlockInactiveFixed(TraceInterval current) { - FixedInterval interval = inactiveFixedList.getFixed(); + FixedInterval interval = inactiveFixedList; while (interval != FixedInterval.EndMarker) { if (current.to() > interval.currentFrom()) { setBlockPos(interval, interval.currentIntersectsAt(current)); @@ -242,7 +378,7 @@ } private void spillCollectActiveAny(RegisterPriority registerPriority) { - TraceInterval interval = activeAnyList.getAny(); + TraceInterval interval = activeAnyList; while (interval != TraceInterval.EndMarker) { setUsePos(interval, Math.min(interval.nextUsage(registerPriority, currentPosition), interval.to()), false); interval = interval.next; @@ -441,7 +577,7 @@ splitPart.setInsertMoveWhenActivated(moveNecessary); assert splitPart.from() >= currentPosition : "cannot append new interval before current walk position"; - unhandledAnyList.addToListSortedByStartAndUsePositions(splitPart); + unhandledAnyList = TraceLinearScanWalker.addToListSortedByStartAndUsePositions(unhandledAnyList, splitPart); if (Debug.isLogEnabled()) { Debug.log("left interval %s: %s", moveNecessary ? " " : "", interval.logString()); @@ -851,7 +987,7 @@ } splitPos = usePos[reg.number]; - interval.assignLocation(reg.asValue(interval.kind())); + interval.assignLocation(reg.asValue(allocator.getKind(interval))); if (Debug.isLogEnabled()) { Debug.log("selected register %d (%s)", reg.number, reg); } @@ -978,7 +1114,7 @@ assert splitPos > 0 : "invalid splitPos"; assert needSplit || splitPos > interval.from() : "splitting interval at from"; - interval.assignLocation(reg.asValue(interval.kind())); + interval.assignLocation(reg.asValue(allocator.getKind(interval))); if (needSplit) { // register not available for full interval : so split it splitWhenPartialRegisterAvailable(interval, splitPos); @@ -990,7 +1126,7 @@ } } - protected void dumpLIRAndIntervals(String description) { + private void dumpLIRAndIntervals(String description) { Debug.dump(Debug.INFO_LEVEL, allocator.getLIR(), description); allocator.printIntervals(description); } @@ -1035,7 +1171,7 @@ } private void initVarsForAlloc(TraceInterval interval) { - AllocatableRegisters allocatableRegisters = allocator.getRegisterAllocationConfig().getAllocatableRegisters(interval.kind().getPlatformKind()); + AllocatableRegisters allocatableRegisters = allocator.getRegisterAllocationConfig().getAllocatableRegisters(allocator.getKind(interval).getPlatformKind()); availableRegs = allocatableRegisters.allocatableRegisters; minReg = allocatableRegisters.minRegisterNumber; maxReg = allocatableRegisters.maxRegisterNumber; @@ -1045,7 +1181,8 @@ if (ValueMoveOp.isValueMoveOp(op)) { ValueMoveOp move = ValueMoveOp.asValueMoveOp(op); if (isVariable(move.getInput()) && isVariable(move.getResult())) { - return move.getInput() != null && move.getInput().equals(from.operand) && move.getResult() != null && move.getResult().equals(to.operand); + return move.getInput() != null && LIRValueUtil.asVariable(move.getInput()).index == from.operandNumber && move.getResult() != null && + LIRValueUtil.asVariable(move.getResult()).index == to.operandNumber; } } return false; @@ -1111,9 +1248,8 @@ } // allocate a physical register or memory location to an interval - @Override @SuppressWarnings("try") - protected boolean activateCurrent(TraceInterval interval) { + private boolean activateCurrent(TraceInterval interval) { if (Debug.isLogEnabled()) { logCurrentStatus(); } @@ -1121,7 +1257,6 @@ try (Indent indent = Debug.logAndIndent("activating interval %s, splitParent: %d", interval, interval.splitParent().operandNumber)) { - final Value operand = interval.operand; if (interval.location() != null && isStackSlotValue(interval.location())) { // activating an interval that has a stack slot assigned . split it at first use // position @@ -1161,7 +1296,7 @@ if (interval.insertMoveWhenActivated()) { assert interval.isSplitChild(); assert interval.currentSplitChild() != null; - assert !interval.currentSplitChild().operand.equals(operand) : "cannot insert move between same interval"; + assert interval.currentSplitChild().operandNumber != interval.operandNumber : "cannot insert move between same interval"; if (Debug.isLogEnabled()) { Debug.log("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber); } @@ -1179,4 +1314,219 @@ // must be called when all intervals are allocated moveResolver.resolveAndAppendMoves(); } + + @SuppressWarnings("try") + private void logCurrentStatus() { + try (Indent i = Debug.logAndIndent("active:")) { + logList(activeFixedList); + logList(activeAnyList); + } + try (Indent i = Debug.logAndIndent("inactive(fixed):")) { + logList(inactiveFixedList); + } + } + + void walk() { + walkTo(Integer.MAX_VALUE); + } + + private void removeFromList(TraceInterval interval) { + activeAnyList = TraceLinearScanWalker.removeAny(activeAnyList, interval); + } + + /** + * Walks up to {@code from} and updates the state of {@link FixedInterval fixed intervals}. + * + * Fixed intervals can switch back and forth between the states {@link State#Active} and + * {@link State#Inactive} (and eventually to {@link State#Handled} but handled intervals are not + * managed). + */ + @SuppressWarnings("try") + private void walkToFixed(State state, int from) { + assert state == State.Active || state == State.Inactive : "wrong state"; + FixedInterval prevprev = null; + FixedInterval prev = (state == State.Active) ? activeFixedList : inactiveFixedList; + FixedInterval next = prev; + if (Debug.isLogEnabled()) { + try (Indent i = Debug.logAndIndent("walkToFixed(%s, %d):", state, from)) { + logList(next); + } + } + while (next.currentFrom() <= from) { + FixedInterval cur = next; + next = cur.next; + + boolean rangeHasChanged = false; + while (cur.currentTo() <= from) { + cur.nextRange(); + rangeHasChanged = true; + } + + // also handle move from inactive list to active list + rangeHasChanged = rangeHasChanged || (state == State.Inactive && cur.currentFrom() <= from); + + if (rangeHasChanged) { + // remove cur from list + if (prevprev == null) { + if (state == State.Active) { + activeFixedList = next; + } else { + inactiveFixedList = next; + } + } else { + prevprev.next = next; + } + prev = next; + TraceInterval.State newState; + if (cur.currentAtEnd()) { + // move to handled state (not maintained as a list) + newState = State.Handled; + } else { + if (cur.currentFrom() <= from) { + // sort into active list + activeFixedList = TraceLinearScanWalker.addToListSortedByCurrentFromPositions(activeFixedList, cur); + newState = State.Active; + } else { + // sort into inactive list + inactiveFixedList = TraceLinearScanWalker.addToListSortedByCurrentFromPositions(inactiveFixedList, cur); + newState = State.Inactive; + } + if (prev == cur) { + assert state == newState; + prevprev = prev; + prev = cur.next; + } + } + intervalMoved(cur, state, newState); + } else { + prevprev = prev; + prev = cur.next; + } + } + } + + /** + * Walks up to {@code from} and updates the state of {@link TraceInterval intervals}. + * + * Trace intervals can switch once from {@link State#Unhandled} to {@link State#Active} and then + * to {@link State#Handled} but handled intervals are not managed. + */ + @SuppressWarnings("try") + private void walkToAny(int from) { + TraceInterval prevprev = null; + TraceInterval prev = activeAnyList; + TraceInterval next = prev; + if (Debug.isLogEnabled()) { + try (Indent i = Debug.logAndIndent("walkToAny(%d):", from)) { + logList(next); + } + } + while (next.from() <= from) { + TraceInterval cur = next; + next = cur.next; + + if (cur.to() <= from) { + // remove cur from list + if (prevprev == null) { + activeAnyList = next; + } else { + prevprev.next = next; + } + intervalMoved(cur, State.Active, State.Handled); + } else { + prevprev = prev; + } + prev = next; + } + } + + /** + * Get the next interval from {@linkplain #unhandledAnyList} which starts before or at + * {@code toOpId}. The returned interval is removed. + * + * @postcondition all intervals in {@linkplain #unhandledAnyList} start after {@code toOpId}. + * + * @return The next interval or null if there is no {@linkplain #unhandledAnyList unhandled} + * interval at position {@code toOpId}. + */ + private TraceInterval nextInterval(int toOpId) { + TraceInterval any = unhandledAnyList; + + if (any != TraceInterval.EndMarker) { + TraceInterval currentInterval = unhandledAnyList; + if (toOpId < currentInterval.from()) { + return null; + } + + unhandledAnyList = currentInterval.next; + currentInterval.next = TraceInterval.EndMarker; + return currentInterval; + } + return null; + + } + + /** + * Walk up to {@code toOpId}. + * + * @postcondition {@link #currentPosition} is set to {@code toOpId}, {@link #activeFixedList} + * and {@link #inactiveFixedList} are populated. + */ + @SuppressWarnings("try") + private void walkTo(int toOpId) { + assert currentPosition <= toOpId : "can not walk backwards"; + for (TraceInterval currentInterval = nextInterval(toOpId); currentInterval != null; currentInterval = nextInterval(toOpId)) { + int opId = currentInterval.from(); + + // set currentPosition prior to call of walkTo + currentPosition = opId; + + // update unhandled stack intervals + // updateUnhandledStackIntervals(opId); + + // call walkTo even if currentPosition == id + walkToFixed(State.Active, opId); + walkToFixed(State.Inactive, opId); + walkToAny(opId); + + try (Indent indent = Debug.logAndIndent("walk to op %d", opId)) { + if (activateCurrent(currentInterval)) { + activeAnyList = TraceLinearScanWalker.addToListSortedByFromPositions(activeAnyList, currentInterval); + intervalMoved(currentInterval, State.Unhandled, State.Active); + } + } + } + // set currentPosition prior to call of walkTo + currentPosition = toOpId; + + if (currentPosition <= allocator.maxOpId()) { + // update unhandled stack intervals + // updateUnhandledStackIntervals(toOpId); + + // call walkTo if still in range + walkToFixed(State.Active, toOpId); + walkToFixed(State.Inactive, toOpId); + walkToAny(toOpId); + } + } + + private static void logList(FixedInterval i) { + for (FixedInterval interval = i; interval != FixedInterval.EndMarker; interval = interval.next) { + Debug.log("%s", interval.logString()); + } + } + + private static void logList(TraceInterval i) { + for (TraceInterval interval = i; interval != TraceInterval.EndMarker; interval = interval.next) { + Debug.log("%s", interval.logString()); + } + } + + private static void intervalMoved(IntervalHint interval, State from, State to) { + // intervalMoved() is called whenever an interval moves from one interval list to another. + // In the implementation of this method it is prohibited to move the interval to any list. + if (Debug.isLogEnabled()) { + Debug.log("interval moved from %s to %s: %s", from, to, interval.logString()); + } + } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLocalMoveResolver.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLocalMoveResolver.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLocalMoveResolver.java Thu Jun 01 02:21:18 2017 +0000 @@ -323,11 +323,11 @@ } private void insertMove(TraceInterval fromInterval, TraceInterval toInterval) { - assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind(), allocator.getRegisterAllocationConfig()) : "move between different types"; + assert fromInterval.operandNumber != toInterval.operandNumber : "from and to interval equal: " + fromInterval; + assert LIRKind.verifyMoveKinds(allocator.getKind(toInterval), allocator.getKind(fromInterval), allocator.getRegisterAllocationConfig()) : "move between different types"; assert insertIdx != -1 : "must setup insert position first"; - insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location())); + insertionBuffer.append(insertIdx, createMove(allocator.getOperand(fromInterval), allocator.getOperand(toInterval), fromInterval.location(), toInterval.location())); if (Debug.isLogEnabled()) { Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); @@ -335,8 +335,8 @@ } /** - * @param fromOpr {@link TraceInterval#operand operand} of the {@code from} interval - * @param toOpr {@link TraceInterval#operand operand} of the {@code to} interval + * @param fromOpr {@link TraceInterval operand} of the {@code from} interval + * @param toOpr {@link TraceInterval operand} of the {@code to} interval * @param fromLocation {@link TraceInterval#location() location} of the {@code to} interval * @param toLocation {@link TraceInterval#location() location} of the {@code to} interval */ @@ -350,7 +350,7 @@ private void insertMove(Constant fromOpr, TraceInterval toInterval) { assert insertIdx != -1 : "must setup insert position first"; - AllocatableValue toOpr = toInterval.operand; + AllocatableValue toOpr = allocator.getOperand(toInterval); LIRInstruction move = getAllocator().getSpillMoveFactory().createLoad(toOpr, fromOpr); insertionBuffer.append(insertIdx, move); @@ -436,7 +436,7 @@ // one stack slot to another can happen (not allowed by LIRAssembler AllocatableValue spillSlot1 = fromInterval1.spillSlot(); if (spillSlot1 == null) { - spillSlot1 = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval1.kind()); + spillSlot1 = getAllocator().getFrameMapBuilder().allocateSpillSlot(allocator.getKind(fromInterval1)); fromInterval1.setSpillSlot(spillSlot1); cycleBreakingSlotsAllocated.increment(); } @@ -448,7 +448,7 @@ int stackSpillCandidate = 0; TraceInterval fromInterval = getMappingFrom(stackSpillCandidate); // allocate new stack slot - VirtualStackSlot spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(fromInterval.kind()); + VirtualStackSlot spillSlot = getAllocator().getFrameMapBuilder().allocateSpillSlot(allocator.getKind(fromInterval)); spillInterval(stackSpillCandidate, fromInterval, spillSlot); } @@ -535,9 +535,9 @@ Debug.log("add move mapping from %s to %s", fromInterval, toInterval); } - assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; - assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind(), allocator.getRegisterAllocationConfig()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), - toInterval.kind(), fromInterval, toInterval); + assert fromInterval.operandNumber != toInterval.operandNumber : "from and to interval equal: " + fromInterval; + assert LIRKind.verifyMoveKinds(allocator.getKind(toInterval), allocator.getKind(fromInterval), allocator.getRegisterAllocationConfig()) : String.format( + "Kind mismatch: %s vs. %s, from=%s, to=%s", allocator.getKind(fromInterval), allocator.getKind(toInterval), fromInterval, toInterval); mappingFrom.add(fromInterval); mappingFromOpr.add(null); mappingTo.add(toInterval); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java Thu Jun 01 02:21:18 2017 +0000 @@ -52,6 +52,7 @@ import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodeinfo.Verbosity; +import org.graalvm.compiler.nodes.java.ExceptionObjectNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; import org.graalvm.compiler.nodes.virtual.EscapeObjectState; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; @@ -96,7 +97,7 @@ /** * @see BytecodeFrame#rethrowException */ - protected boolean rethrowException; + protected final boolean rethrowException; protected final boolean duringCall; @@ -184,15 +185,17 @@ /** * Creates a placeholder frame state with a single element on the stack representing a return - * value. This allows the parsing of an intrinsic to communicate the returned value in a - * {@link StateSplit#stateAfter() stateAfter} to the inlining call site. + * value or thrown exception. This allows the parsing of an intrinsic to communicate the + * returned or thrown value in a {@link StateSplit#stateAfter() stateAfter} to the inlining call + * site. * * @param bci this must be {@link BytecodeFrame#AFTER_BCI} */ - public FrameState(int bci, ValueNode returnValue) { - this(null, null, bci, 0, returnValue.getStackKind().getSlotCount(), 0, false, false, null, Collections. emptyList()); - assert bci == BytecodeFrame.AFTER_BCI; - this.values.initialize(0, returnValue); + public FrameState(int bci, ValueNode returnValueOrExceptionObject) { + this(null, null, bci, 0, returnValueOrExceptionObject.getStackKind().getSlotCount(), 0, returnValueOrExceptionObject instanceof ExceptionObjectNode, false, null, + Collections. emptyList()); + assert (bci == BytecodeFrame.AFTER_BCI && !rethrowException()) || (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && rethrowException()); + this.values.initialize(0, returnValueOrExceptionObject); } public FrameState(FrameState outerFrameState, Bytecode code, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, ValueNode[] locks, List monitorIds, diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,20 +22,7 @@ */ package org.graalvm.compiler.nodes; -import static org.graalvm.compiler.nodeinfo.InputType.Extension; -import static org.graalvm.compiler.nodeinfo.InputType.Memory; -import static org.graalvm.compiler.nodeinfo.InputType.State; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_64; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; - -import java.util.Map; - +import jdk.vm.ci.meta.JavaKind; import org.graalvm.api.word.LocationIdentity; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.Node; @@ -55,7 +42,19 @@ import org.graalvm.compiler.nodes.spi.UncheckedInterfaceProvider; import org.graalvm.compiler.nodes.util.GraphUtil; -import jdk.vm.ci.meta.JavaKind; +import java.util.Map; + +import static org.graalvm.compiler.nodeinfo.InputType.Extension; +import static org.graalvm.compiler.nodeinfo.InputType.Memory; +import static org.graalvm.compiler.nodeinfo.InputType.State; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_64; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_64; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; /** * The {@code InvokeNode} represents all kinds of method calls. diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,14 +22,7 @@ */ package org.graalvm.compiler.nodes; -import static org.graalvm.compiler.nodeinfo.InputType.Extension; -import static org.graalvm.compiler.nodeinfo.InputType.Memory; -import static org.graalvm.compiler.nodeinfo.InputType.State; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; - -import java.util.Map; - +import jdk.vm.ci.meta.JavaKind; import org.graalvm.api.word.LocationIdentity; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.Node; @@ -45,7 +38,13 @@ import org.graalvm.compiler.nodes.spi.UncheckedInterfaceProvider; import org.graalvm.compiler.nodes.util.GraphUtil; -import jdk.vm.ci.meta.JavaKind; +import java.util.Map; + +import static org.graalvm.compiler.nodeinfo.InputType.Extension; +import static org.graalvm.compiler.nodeinfo.InputType.Memory; +import static org.graalvm.compiler.nodeinfo.InputType.State; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN; @NodeInfo(nameTemplate = "Invoke!#{p#targetMethod/s}", allowedUsageTypes = {Memory}, cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN) public final class InvokeWithExceptionNode extends ControlSplitNode implements Invoke, MemoryCheckpoint.Single, LIRLowerable, UncheckedInterfaceProvider { @@ -198,12 +197,30 @@ GraphUtil.killCFG(edge); } + public void replaceWithNewBci(int newBci) { + AbstractBeginNode nextNode = next(); + AbstractBeginNode exceptionObject = exceptionEdge; + setExceptionEdge(null); + setNext(null); + InvokeWithExceptionNode repl = graph().add(new InvokeWithExceptionNode(callTarget(), exceptionObject, newBci)); + repl.setStateAfter(stateAfter); + this.setStateAfter(null); + this.replaceAtPredecessor(repl); + repl.setNext(nextNode); + boolean removed = this.callTarget().removeUsage(this); + assert removed; + this.replaceAtUsages(repl); + this.markDeleted(); + } + @Override public void intrinsify(Node node) { assert !(node instanceof ValueNode) || (((ValueNode) node).getStackKind() == JavaKind.Void) == (getStackKind() == JavaKind.Void); CallTargetNode call = callTarget; FrameState state = stateAfter(); - killExceptionEdge(); + if (exceptionEdge != null) { + killExceptionEdge(); + } if (node instanceof StateSplit) { StateSplit stateSplit = (StateSplit) node; stateSplit.setStateAfter(state); @@ -281,4 +298,16 @@ public int getSuccessorCount() { return 2; } + + /** + * Replaces this InvokeWithExceptionNode with a normal InvokeNode. Kills the exception dispatch + * code. + */ + public InvokeNode replaceWithInvoke() { + InvokeNode invokeNode = graph().add(new InvokeNode(callTarget, bci)); + AbstractBeginNode oldException = this.exceptionEdge; + graph().replaceSplitWithFixed(this, invokeNode, this.next()); + GraphUtil.killCFG(oldException); + return invokeNode; + } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -96,7 +96,9 @@ SignedRemNode integerRemNode = (SignedRemNode) integerSubNode.getY(); if (integerSubNode.stamp().isCompatible(this.stamp()) && integerRemNode.stamp().isCompatible(this.stamp()) && integerSubNode.getX() == integerRemNode.getX() && forY == integerRemNode.getY()) { - return new SignedDivNode(integerSubNode.getX(), forY); + SignedDivNode sd = new SignedDivNode(integerSubNode.getX(), forY); + sd.stateBefore = this.stateBefore; + return sd; } } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/IntrinsicContext.java Thu Jun 01 02:21:18 2017 +0000 @@ -23,21 +23,21 @@ package org.graalvm.compiler.nodes.graphbuilderconf; import static jdk.vm.ci.code.BytecodeFrame.AFTER_BCI; +import static jdk.vm.ci.code.BytecodeFrame.AFTER_EXCEPTION_BCI; import static jdk.vm.ci.code.BytecodeFrame.BEFORE_BCI; import static jdk.vm.ci.code.BytecodeFrame.INVALID_FRAMESTATE_BCI; -import static jdk.vm.ci.code.BytecodeFrame.UNKNOWN_BCI; import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING; import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION; import org.graalvm.compiler.bytecode.BytecodeProvider; +import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.StructuredGraph; -import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.java.ExceptionObjectNode; -import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.meta.ResolvedJavaMethod; /** @@ -98,41 +98,12 @@ final CompilationContext compilationContext; - private final ValueNode[] args; - - /** - * Gets the arguments to the intrinsic invocation (if available). - * - * @return {@code null} if the arguments to the intrinsic invocation are not available - */ - public ValueNode[] getArgs() { - return args; - } - - /** - * Gets the bytecode index of the intrinsic invocation (if available). - * - * @return {@value BytecodeFrame#UNKNOWN_BCI} if the bytecode index of the intrinsic invocation - * is not available - */ - public int bci() { - return bci; - } - - private final int bci; - public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext) { - this(method, intrinsic, bytecodeProvider, compilationContext, null, UNKNOWN_BCI); - } - - public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, BytecodeProvider bytecodeProvider, CompilationContext compilationContext, ValueNode[] args, int bci) { this.method = method; this.intrinsic = intrinsic; this.bytecodeProvider = bytecodeProvider; assert bytecodeProvider != null; this.compilationContext = compilationContext; - this.args = args; - this.bci = bci; assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)"); } @@ -187,28 +158,40 @@ void addSideEffect(StateSplit sideEffect); } - public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit) { + public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit, NodeSourcePosition sourcePosition) { assert forStateSplit != graph.start(); if (forStateSplit.hasSideEffect()) { if (sideEffects.isAfterSideEffect()) { // Only the last side effect on any execution path in a replacement // can inherit the stateAfter of the replaced node FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI)); + invalid.setNodeSourcePosition(sourcePosition); for (StateSplit lastSideEffect : sideEffects.sideEffects()) { lastSideEffect.setStateAfter(invalid); } } sideEffects.addSideEffect(forStateSplit); - return graph.add(new FrameState(AFTER_BCI)); + FrameState frameState; + if (forStateSplit instanceof ExceptionObjectNode) { + frameState = graph.add(new FrameState(AFTER_EXCEPTION_BCI, (ExceptionObjectNode) forStateSplit)); + } else { + frameState = graph.add(new FrameState(AFTER_BCI)); + } + frameState.setNodeSourcePosition(sourcePosition); + return frameState; } else { if (forStateSplit instanceof AbstractMergeNode) { // Merge nodes always need a frame state if (sideEffects.isAfterSideEffect()) { // A merge after one or more side effects - return graph.add(new FrameState(AFTER_BCI)); + FrameState frameState = graph.add(new FrameState(AFTER_BCI)); + frameState.setNodeSourcePosition(sourcePosition); + return frameState; } else { // A merge before any side effects - return graph.add(new FrameState(BEFORE_BCI)); + FrameState frameState = graph.add(new FrameState(BEFORE_BCI)); + frameState.setNodeSourcePosition(sourcePosition); + return frameState; } } else { // Other non-side-effects do not need a state diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java Thu Jun 01 02:21:18 2017 +0000 @@ -709,14 +709,42 @@ return null; } + @SuppressWarnings("serial") + static class InvocationPlugRegistrationError extends GraalError { + InvocationPlugRegistrationError(Throwable cause) { + super(cause); + } + } + private void flushDeferrables() { if (deferredRegistrations != null) { synchronized (this) { if (deferredRegistrations != null) { - for (Runnable deferrable : deferredRegistrations) { - deferrable.run(); + try { + for (Runnable deferrable : deferredRegistrations) { + deferrable.run(); + } + deferredRegistrations = null; + } catch (InvocationPlugRegistrationError t) { + throw t; + } catch (Throwable t) { + /* + * Something went wrong during registration but it's possible we'll end up + * coming back into this code. nulling out deferredRegistrations would just + * cause other things to break and rerunning them would cause errors about + * already registered plugins, so rethrow the original exception during + * later invocations. + */ + deferredRegistrations.clear(); + Runnable rethrow = new Runnable() { + @Override + public void run() { + throw new InvocationPlugRegistrationError(t); + } + }; + deferredRegistrations.add(rethrow); + rethrow.run(); } - deferredRegistrations = null; } } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,10 +22,8 @@ */ package org.graalvm.compiler.nodes.java; -import static org.graalvm.compiler.nodeinfo.InputType.Memory; -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; - +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.MetaAccessProvider; import org.graalvm.api.word.LocationIdentity; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.TypeReference; @@ -41,7 +39,9 @@ import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; -import jdk.vm.ci.meta.MetaAccessProvider; +import static org.graalvm.compiler.nodeinfo.InputType.Memory; +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; /** * The entry to an exception handler with the exception coming from a call (as opposed to a local @@ -60,6 +60,15 @@ return LocationIdentity.any(); } + /** + * An exception handler is an entry point to a method from the runtime and so represents an + * instruction that cannot be re-executed. It therefore needs a frame state. + */ + @Override + public boolean hasSideEffect() { + return true; + } + @Override public void lower(LoweringTool tool) { if (graph().getGuardsStage() == StructuredGraph.GuardsStage.FIXED_DEOPTS) { @@ -83,6 +92,8 @@ @Override public boolean verify() { assertTrue(stateAfter() != null, "an exception handler needs a frame state"); + assertTrue(stateAfter().stackSize() == 1 && stateAfter().stackAt(0).stamp().getStackKind() == JavaKind.Object, + "an exception handler's frame state must have only the exception on the stack"); return super.verify(); } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -400,6 +400,14 @@ MapCursor entries = mergeMap.getEntries(); while (entries.advance()) { ValuePhiNode phi = entries.getKey(); + assert phi.isAlive() || phi.isDeleted(); + /* + * Phi might have been killed already via a conditional elimination in another + * branch. + */ + if (phi.isDeleted()) { + continue; + } PhiInfoElement phiInfoElements = entries.getValue(); Stamp bestPossibleStamp = null; for (int i = 0; i < phi.valueCount(); ++i) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,14 +22,14 @@ */ package org.graalvm.compiler.phases.common.inlining; -import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile; -import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException; -import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining; - -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.List; - +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.meta.Assumptions; +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; import org.graalvm.compiler.api.replacements.MethodSubstitution; import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.type.Stamp; @@ -38,7 +38,6 @@ import org.graalvm.compiler.core.common.util.Util; import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.Debug.Scope; -import org.graalvm.compiler.debug.Fingerprint; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.debug.internal.method.MethodMetricsImpl; import org.graalvm.compiler.debug.internal.method.MethodMetricsInlineeScopeInfo; @@ -48,6 +47,7 @@ import org.graalvm.compiler.graph.Graph.NodeEventScope; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeInputList; +import org.graalvm.compiler.graph.NodeMap; import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.graph.NodeWorkList; import org.graalvm.compiler.nodeinfo.Verbosity; @@ -58,6 +58,7 @@ import org.graalvm.compiler.nodes.CallTargetNode; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.EndNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; @@ -69,6 +70,7 @@ import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; import org.graalvm.compiler.nodes.ParameterNode; +import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StartNode; @@ -95,14 +97,14 @@ import org.graalvm.util.UnmodifiableEconomicMap; import org.graalvm.util.UnmodifiableMapCursor; -import jdk.vm.ci.code.BytecodeFrame; -import jdk.vm.ci.meta.Assumptions; -import jdk.vm.ci.meta.DeoptimizationAction; -import jdk.vm.ci.meta.DeoptimizationReason; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; +import java.lang.reflect.Constructor; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; + +import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile; +import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException; +import static org.graalvm.compiler.core.common.GraalOptions.HotSpotPrintInlining; public class InliningUtil extends ValueMergeUtil { @@ -277,9 +279,6 @@ StructuredGraph graph = invokeNode.graph(); MethodMetricsInlineeScopeInfo m = MethodMetricsInlineeScopeInfo.create(graph.getOptions()); try (Debug.Scope s = Debug.methodMetricsScope("InlineEnhancement", m, false)) { - if (Fingerprint.ENABLED) { - Fingerprint.submit("inlining %s into %s: %s", formatGraph(inlineGraph), formatGraph(invoke.asNode().graph()), inlineGraph.getNodes().snapshot()); - } final NodeInputList parameters = invoke.callTarget().arguments(); assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal(); @@ -291,7 +290,7 @@ ArrayList nodes = new ArrayList<>(inlineGraph.getNodes().count()); ArrayList returnNodes = new ArrayList<>(4); - ArrayList partialIntrinsicExits = new ArrayList<>(); + ArrayList partialIntrinsicExits = new ArrayList<>(); UnwindNode unwindNode = null; final StartNode entryPointNode = inlineGraph.start(); FixedNode firstCFGNode = entryPointNode.next(); @@ -305,10 +304,10 @@ nodes.add(node); if (node instanceof ReturnNode) { returnNodes.add((ReturnNode) node); - } else if (node instanceof InvokeNode) { - InvokeNode invokeInInlineGraph = (InvokeNode) node; + } else if (node instanceof Invoke) { + Invoke invokeInInlineGraph = (Invoke) node; if (invokeInInlineGraph.bci() == BytecodeFrame.UNKNOWN_BCI) { - ResolvedJavaMethod target1 = invoke.callTarget().targetMethod(); + ResolvedJavaMethod target1 = inlineeMethod; ResolvedJavaMethod target2 = invokeInInlineGraph.callTarget().targetMethod(); assert target1.equals(target2) : String.format("invoke in inlined method expected to be partial intrinsic exit (i.e., call to %s), not a call to %s", target1.format("%H.%n(%p)"), target2.format("%H.%n(%p)")); @@ -338,7 +337,7 @@ assert invokeNode.successors().first() != null : invoke; assert invokeNode.predecessor() != null; - UnmodifiableEconomicMap duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement); + EconomicMap duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement); FrameState stateAfter = invoke.stateAfter(); assert stateAfter == null || stateAfter.isAlive(); @@ -370,12 +369,16 @@ for (int i = 0; i < returnNodes.size(); i++) { returnNodes.set(i, (ReturnNode) duplicates.get(returnNodes.get(i))); } - for (InvokeNode exit : partialIntrinsicExits) { + for (Invoke exit : partialIntrinsicExits) { // A partial intrinsic exit must be replaced with a call to // the intrinsified method. - InvokeNode dup = (InvokeNode) duplicates.get(exit); - InvokeNode repl = graph.add(new InvokeNode(invoke.callTarget(), invoke.bci())); - dup.intrinsify(repl); + Invoke dup = (Invoke) duplicates.get(exit.asNode()); + if (dup instanceof InvokeNode) { + InvokeNode repl = graph.add(new InvokeNode(invoke.callTarget(), invoke.bci())); + dup.intrinsify(repl.asNode()); + } else { + ((InvokeWithExceptionNode) dup).replaceWithNewBci(invoke.bci()); + } } if (unwindNode != null) { unwindNode = (UnwindNode) duplicates.get(unwindNode); @@ -392,7 +395,7 @@ } /** - * Inline {@code inlineGraph} into the current replacoing the node {@code Invoke} and return the + * Inline {@code inlineGraph} into the current replacing the node {@code Invoke} and return the * set of nodes which should be canonicalized. The set should only contain nodes which modified * by the inlining since the current graph and {@code inlineGraph} are expected to already be * canonical. @@ -467,10 +470,13 @@ invokeNode.replaceAtUsages(returnValue); returnNode.replaceAndDelete(n); } else { - AbstractMergeNode merge = graph.add(new MergeNode()); + MergeNode merge = graph.add(new MergeNode()); merge.setStateAfter(stateAfter); returnValue = mergeReturns(merge, returnNodes); invokeNode.replaceAtUsages(returnValue); + if (merge.isPhiAtMerge(returnValue)) { + fixFrameStates(graph, merge, (PhiNode) returnValue); + } merge.setNext(n); } } else { @@ -505,11 +511,56 @@ return returnValue; } - private static String formatGraph(StructuredGraph graph) { - if (graph.method() == null) { - return graph.name; + private static void fixFrameStates(StructuredGraph graph, MergeNode originalMerge, PhiNode returnPhi) { + // It is possible that some of the frame states that came from AFTER_BCI reference a Phi + // node that was created to merge multiple returns. This can create cycles + // (see GR-3949 and GR-3957). + // To detect this, we follow the control paths starting from the merge node, + // split the Phi node inputs at merges and assign the proper input to each frame state. + NodeMap seen = new NodeMap<>(graph); + ArrayDeque workList = new ArrayDeque<>(); + ArrayDeque valueList = new ArrayDeque<>(); + workList.push(originalMerge); + valueList.push(returnPhi); + while (!workList.isEmpty()) { + Node current = workList.pop(); + ValueNode currentValue = valueList.pop(); + if (seen.containsKey(current)) { + continue; + } + seen.put(current, current); + if (current instanceof StateSplit && current != originalMerge) { + StateSplit stateSplit = (StateSplit) current; + FrameState state = stateSplit.stateAfter(); + if (state != null && state.values().contains(returnPhi)) { + int index = 0; + FrameState duplicate = state.duplicate(); + for (ValueNode value : state.values()) { + if (value == returnPhi) { + duplicate.values().set(index, currentValue); + } + index++; + } + stateSplit.setStateAfter(duplicate); + GraphUtil.tryKillUnused(state); + } + } + if (current instanceof AbstractMergeNode) { + AbstractMergeNode currentMerge = (AbstractMergeNode) current; + for (EndNode pred : currentMerge.cfgPredecessors()) { + ValueNode newValue = currentValue; + if (currentMerge.isPhiAtMerge(currentValue)) { + PhiNode currentPhi = (PhiNode) currentValue; + newValue = currentPhi.valueAt(pred); + } + workList.push(pred); + valueList.push(newValue); + } + } else if (current.predecessor() != null) { + workList.push(current.predecessor()); + valueList.push(currentValue); + } } - return graph.method().format("%H.%n(%p)"); } @SuppressWarnings("try") @@ -546,58 +597,35 @@ } } - protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap duplicates, FrameState stateAtExceptionEdge, + protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, EconomicMap duplicates, FrameState stateAtExceptionEdge, boolean alwaysDuplicateStateAfter) { FrameState stateAtReturn = invoke.stateAfter(); FrameState outerFrameState = null; JavaKind invokeReturnKind = invoke.asNode().getStackKind(); + EconomicMap replacements = EconomicMap.create(); for (FrameState original : inlineGraph.getNodes(FrameState.TYPE)) { FrameState frameState = (FrameState) duplicates.get(original); if (frameState != null && frameState.isAlive()) { if (outerFrameState == null) { outerFrameState = stateAtReturn.duplicateModifiedDuringCall(invoke.bci(), invokeReturnKind); } - processFrameState(frameState, invoke, inlineGraph.method(), stateAtExceptionEdge, outerFrameState, alwaysDuplicateStateAfter, invoke.callTarget().targetMethod(), + processFrameState(frameState, invoke, replacements, inlineGraph.method(), stateAtExceptionEdge, outerFrameState, alwaysDuplicateStateAfter, invoke.callTarget().targetMethod(), invoke.callTarget().arguments()); } } + // If processing the frame states replaced any nodes, update the duplicates map. + duplicates.replaceAll((key, value) -> replacements.containsKey(value) ? replacements.get(value) : value); } - public static FrameState processFrameState(FrameState frameState, Invoke invoke, ResolvedJavaMethod inlinedMethod, FrameState stateAtExceptionEdge, FrameState outerFrameState, + public static FrameState processFrameState(FrameState frameState, Invoke invoke, EconomicMap replacements, ResolvedJavaMethod inlinedMethod, FrameState stateAtExceptionEdge, + FrameState outerFrameState, boolean alwaysDuplicateStateAfter, ResolvedJavaMethod invokeTargetMethod, List invokeArgsList) { - assert outerFrameState == null || !outerFrameState.isDeleted() : outerFrameState; - FrameState stateAtReturn = invoke.stateAfter(); + final FrameState stateAtReturn = invoke.stateAfter(); JavaKind invokeReturnKind = invoke.asNode().getStackKind(); if (frameState.bci == BytecodeFrame.AFTER_BCI) { - FrameState stateAfterReturn = stateAtReturn; - if (frameState.getCode() == null) { - // This is a frame state for a side effect within an intrinsic - // that was parsed for post-parse intrinsification - for (Node usage : frameState.usages()) { - if (usage instanceof ForeignCallNode) { - // A foreign call inside an intrinsic needs to have - // the BCI of the invoke being intrinsified - ForeignCallNode foreign = (ForeignCallNode) usage; - foreign.setBci(invoke.bci()); - } - } - } - - // pop return kind from invoke's stateAfter and replace with this frameState's return - // value (top of stack) - if (frameState.stackSize() > 0 && (alwaysDuplicateStateAfter || stateAfterReturn.stackAt(0) != frameState.stackAt(0))) { - stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, invokeReturnKind, frameState.stackAt(0)); - } - - // Return value does no longer need to be limited by the monitor exit. - for (MonitorExitNode n : frameState.usages().filter(MonitorExitNode.class)) { - n.clearEscapedReturnValue(); - } - - frameState.replaceAndDelete(stateAfterReturn); - return stateAfterReturn; + return handleAfterBciFrameState(frameState, invoke, alwaysDuplicateStateAfter); } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) { // pop exception object from invoke's stateAfter and replace with this frameState's // exception object (top of stack) @@ -608,7 +636,8 @@ frameState.replaceAndDelete(stateAfterException); return stateAfterException; } else if (frameState.bci == BytecodeFrame.UNWIND_BCI || frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI) { - return handleMissingAfterExceptionFrameState(frameState); + handleMissingAfterExceptionFrameState(frameState, invoke, replacements, alwaysDuplicateStateAfter); + return frameState; } else if (frameState.bci == BytecodeFrame.BEFORE_BCI) { // This is an intrinsic. Deoptimizing within an intrinsic // must re-execute the intrinsified invocation @@ -628,6 +657,44 @@ } } + private static FrameState handleAfterBciFrameState(FrameState frameState, Invoke invoke, boolean alwaysDuplicateStateAfter) { + FrameState stateAtReturn = invoke.stateAfter(); + JavaKind invokeReturnKind = invoke.asNode().getStackKind(); + FrameState stateAfterReturn = stateAtReturn; + if (frameState.getCode() == null) { + // This is a frame state for a side effect within an intrinsic + // that was parsed for post-parse intrinsification + for (Node usage : frameState.usages()) { + if (usage instanceof ForeignCallNode) { + // A foreign call inside an intrinsic needs to have + // the BCI of the invoke being intrinsified + ForeignCallNode foreign = (ForeignCallNode) usage; + foreign.setBci(invoke.bci()); + } + } + } + + // pop return kind from invoke's stateAfter and replace with this frameState's return + // value (top of stack) + assert !frameState.rethrowException() : frameState; + if (frameState.stackSize() > 0 && (alwaysDuplicateStateAfter || stateAfterReturn.stackAt(0) != frameState.stackAt(0))) { + // A non-void return value. + stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, invokeReturnKind, frameState.stackAt(0)); + } else { + // A void return value. + stateAfterReturn = stateAtReturn.duplicate(); + } + assert stateAfterReturn.bci != BytecodeFrame.UNKNOWN_BCI; + + // Return value does no longer need to be limited by the monitor exit. + for (MonitorExitNode n : frameState.usages().filter(MonitorExitNode.class)) { + n.clearEscapedReturnValue(); + } + + frameState.replaceAndDelete(stateAfterReturn); + return stateAfterReturn; + } + static boolean checkInlineeFrameState(Invoke invoke, ResolvedJavaMethod inlinedMethod, FrameState frameState) { assert frameState.bci != BytecodeFrame.AFTER_EXCEPTION_BCI : frameState; assert frameState.bci != BytecodeFrame.BEFORE_BCI : frameState; @@ -663,7 +730,7 @@ return frameState.bci == BytecodeFrame.AFTER_EXCEPTION_BCI || (frameState.bci == BytecodeFrame.UNWIND_BCI && !frameState.getMethod().isSynchronized()); } - public static FrameState handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState) { + public static FrameState handleMissingAfterExceptionFrameState(FrameState nonReplaceableFrameState, Invoke invoke, EconomicMap replacements, boolean alwaysDuplicateStateAfter) { Graph graph = nonReplaceableFrameState.graph(); NodeWorkList workList = graph.createNodeWorkList(); workList.add(nonReplaceableFrameState); @@ -686,6 +753,20 @@ end.replaceAtPredecessor(deoptimizeNode); GraphUtil.killCFG(end); } + } else if (fixedStateSplit instanceof ExceptionObjectNode) { + // The target invoke does not have an exception edge. This means that the + // bytecode parser made the wrong assumption of making an + // InvokeWithExceptionNode for the partial intrinsic exit. We therefore + // replace the InvokeWithExceptionNode with a normal + // InvokeNode -- the deoptimization occurs when the invoke throws. + InvokeWithExceptionNode oldInvoke = (InvokeWithExceptionNode) fixedStateSplit.predecessor(); + FrameState oldFrameState = oldInvoke.stateAfter(); + InvokeNode newInvoke = oldInvoke.replaceWithInvoke(); + newInvoke.setStateAfter(oldFrameState.duplicate()); + if (replacements != null) { + replacements.put(oldInvoke, newInvoke); + } + handleAfterBciFrameState(newInvoke.stateAfter(), invoke, alwaysDuplicateStateAfter); } else { FixedNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); if (fixedStateSplit instanceof AbstractBeginNode) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -30,7 +30,6 @@ import org.graalvm.compiler.debug.DebugCounter; import org.graalvm.compiler.debug.DebugMemUseTracker; import org.graalvm.compiler.debug.DebugTimer; -import org.graalvm.compiler.debug.Fingerprint; import org.graalvm.compiler.debug.GraalDebugConfig; import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.Graph.Mark; @@ -196,10 +195,6 @@ if (dumpGraph && Debug.isEnabled()) { dumpAfter(graph, isTopLevel, dumpedBefore); } - if (Fingerprint.ENABLED) { - String graphDesc = graph.method() == null ? graph.name : graph.method().format("%H.%n(%p)"); - Fingerprint.submit("After phase %s nodes in %s are %s", getName(), graphDesc, graph.getNodes().snapshot()); - } if (Debug.isVerifyEnabled()) { Debug.verify(graph, "%s", getName()); } @@ -273,24 +268,14 @@ } protected CharSequence getName() { - String className = BasePhase.this.getClass().getName(); - String s = className.substring(className.lastIndexOf(".") + 1); // strip the package name - int innerClassPos = s.indexOf('$'); - if (innerClassPos > 0) { - /* Remove inner class name. */ - s = s.substring(0, innerClassPos); - } - if (s.endsWith("Phase")) { - s = s.substring(0, s.length() - "Phase".length()); - } - return s; + return new ClassTypeSequence(BasePhase.this.getClass()); } protected abstract void run(StructuredGraph graph, C context); @Override public String contractorName() { - return (String) getName(); + return getName().toString(); } @Override diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/ClassTypeSequence.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/ClassTypeSequence.java Thu Jun 01 02:21:18 2017 +0000 @@ -0,0 +1,96 @@ +/* + * 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.phases; + +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaType; + +final class ClassTypeSequence implements JavaType, CharSequence { + private final Class clazz; + + ClassTypeSequence(Class clazz) { + this.clazz = clazz; + } + + @Override + public String getName() { + return "L" + clazz.getName().replace('.', '/') + ";"; + } + + @Override + public String toJavaName() { + return toJavaName(true); + } + + @Override + public String toJavaName(boolean qualified) { + if (qualified) { + return clazz.getName(); + } else { + int lastDot = clazz.getName().lastIndexOf('.'); + return clazz.getName().substring(lastDot + 1); + } + } + + @Override + public JavaType getComponentType() { + return null; + } + + @Override + public JavaType getArrayClass() { + return null; + } + + @Override + public JavaKind getJavaKind() { + return JavaKind.Object; + } + + @Override + public ResolvedJavaType resolve(ResolvedJavaType accessingClass) { + throw new UnsupportedOperationException(); + } + + @Override + public int length() { + return clazz.getName().length(); + } + + @Override + public char charAt(int index) { + return clazz.getName().charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return clazz.getName().subSequence(start, end); + } + + @Override + public String toString() { + return clazz.getName(); + } +} diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,14 +22,6 @@ */ package org.graalvm.compiler.phases.schedule; -import static org.graalvm.compiler.core.common.GraalOptions.OptScheduleOutOfLoops; -import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Formatter; -import java.util.List; - import org.graalvm.api.word.LocationIdentity; import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.SuppressFBWarnings; @@ -51,6 +43,7 @@ import org.graalvm.compiler.nodes.ControlSplitNode; import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.GuardNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.KillingBeginNode; @@ -75,7 +68,14 @@ import org.graalvm.compiler.nodes.spi.ValueProxy; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.Phase; -import org.graalvm.compiler.nodes.FixedWithNextNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Formatter; +import java.util.List; + +import static org.graalvm.compiler.core.common.GraalOptions.OptScheduleOutOfLoops; +import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates; public final class SchedulePhase extends Phase { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java Thu Jun 01 02:21:18 2017 +0000 @@ -39,6 +39,7 @@ import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.DebugMethodMetrics; import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.nodes.CallTargetNode; @@ -243,7 +244,7 @@ protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, List args, ResolvedJavaMethod verifiedCallee, Integer dumpLevel) throws org.graalvm.compiler.phases.VerifyPhase.VerificationError { ResolvedJavaType arg1Type = ((ObjectStamp) args.get(1).stamp()).type(); - if (metaAccess.lookupJavaType(StructuredGraph.class).isAssignableFrom(arg1Type)) { + if (metaAccess.lookupJavaType(Graph.class).isAssignableFrom(arg1Type)) { verifyStructuredGraphDumping(callerGraph, debugCallTarget, verifiedCallee, dumpLevel); } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java Thu Jun 01 02:21:18 2017 +0000 @@ -38,6 +38,7 @@ import java.util.Map.Entry; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.bytecode.Bytecode; import org.graalvm.compiler.core.common.cfg.BlockMap; import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.GraalDebugConfig.Options; @@ -170,10 +171,20 @@ return snippetReflection; } + @SuppressWarnings("all") @Override - public void print(Graph graph, String title, Map properties) throws IOException { + public void print(Graph graph, Map properties, int id, String format, Object... args) throws IOException { writeByte(BEGIN_GRAPH); - writePoolObject(title); + if (CURRENT_MAJOR_VERSION == 3) { + writeInt(id); + writeString(format); + writeInt(args.length); + for (Object a : args) { + writePropertyObject(a); + } + } else { + writePoolObject(String.format(format, args)); + } writeGraph(graph, properties); flush(); } @@ -324,7 +335,7 @@ writeByte(POOL_CLASS); } else if (object instanceof NodeClass) { writeByte(POOL_NODE_CLASS); - } else if (object instanceof ResolvedJavaMethod) { + } else if (object instanceof ResolvedJavaMethod || object instanceof Bytecode) { writeByte(POOL_METHOD); } else if (object instanceof ResolvedJavaField) { writeByte(POOL_FIELD); @@ -344,6 +355,7 @@ return getClassName(klass.getComponentType()) + "[]"; } + @SuppressWarnings("all") private void addPoolEntry(Object object) throws IOException { char index = constantPool.add(object); writeByte(POOL_NEW); @@ -374,13 +386,22 @@ } else if (object instanceof NodeClass) { NodeClass nodeClass = (NodeClass) object; writeByte(POOL_NODE_CLASS); - writeString(nodeClass.getJavaClass().getSimpleName()); + if (CURRENT_MAJOR_VERSION == 3) { + writePoolObject(nodeClass.getJavaClass()); + } else { + writeString(nodeClass.getJavaClass().getSimpleName()); + } writeString(nodeClass.getNameTemplate()); writeEdgesInfo(nodeClass, Inputs); writeEdgesInfo(nodeClass, Successors); - } else if (object instanceof ResolvedJavaMethod) { + } else if (object instanceof ResolvedJavaMethod || object instanceof Bytecode) { writeByte(POOL_METHOD); - ResolvedJavaMethod method = ((ResolvedJavaMethod) object); + ResolvedJavaMethod method; + if (object instanceof Bytecode) { + method = ((Bytecode) object).getMethod(); + } else { + method = ((ResolvedJavaMethod) object); + } writePoolObject(method.getDeclaringClass()); writePoolObject(method.getName()); writePoolObject(method.getSignature()); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CFGPrinterObserver.java Thu Jun 01 02:21:18 2017 +0000 @@ -79,7 +79,8 @@ } @Override - public void dump(Object object, String message) { + public void dump(Object object, String format, Object... arguments) { + String message = String.format(format, arguments); try { dumpSandboxed(object, message); } catch (Throwable ex) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/CanonicalStringGraphPrinter.java Thu Jun 01 02:21:18 2017 +0000 @@ -258,7 +258,7 @@ } @Override - public void print(Graph graph, String title, Map properties) throws IOException { + public void print(Graph graph, Map properties, int id, String format, Object... args) throws IOException { if (graph instanceof StructuredGraph) { OptionValues options = graph.getOptions(); StructuredGraph structuredGraph = (StructuredGraph) graph; @@ -267,6 +267,7 @@ TTY.println("Dumping string graphs in %s", this.root); this.root = null; } + String title = id + ": " + String.format(format, args); Path filePath = currentDirectory.resolve(escapeFileName(title)); try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filePath.toFile())))) { switch (PrintCanonicalGraphStringFlavor.getValue(options)) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugConfigCustomizer.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugConfigCustomizer.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugConfigCustomizer.java Thu Jun 01 02:21:18 2017 +0000 @@ -80,7 +80,7 @@ private static class NodeDumper implements DebugDumpHandler { @Override - public void dump(Object object, String message) { + public void dump(Object object, String format, Object... arguments) { if (object instanceof Node) { String location = GraphUtil.approxSourceLocation((Node) object); String node = ((Node) object).toString(Verbosity.Debugger); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinter.java Thu Jun 01 02:21:18 2017 +0000 @@ -53,7 +53,7 @@ * Prints an entire {@link Graph} with the specified title, optionally using short names for * nodes. */ - void print(Graph graph, String title, Map properties) throws IOException; + void print(Graph graph, Map properties, int id, String format, Object... args) throws IOException; SnippetReflectionProvider getSnippetReflectionProvider(); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraphPrinterDumpHandler.java Thu Jun 01 02:21:18 2017 +0000 @@ -120,7 +120,7 @@ @Override @SuppressWarnings("try") - public void dump(Object object, final String message) { + public void dump(Object object, final String format, Object... arguments) { if (object instanceof Graph && Options.PrintGraph.getValue(DebugScope.getConfig().getOptions())) { ensureInitialized(); if (printer == null) { @@ -184,7 +184,7 @@ } } addCFGFileName(properties); - printer.print(graph, nextDumpId() + ":" + message, properties); + printer.print(graph, properties, nextDumpId(), format, arguments); } catch (IOException e) { handleException(e); } catch (Throwable e) { diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/IdealGraphPrinter.java Thu Jun 01 02:21:18 2017 +0000 @@ -114,7 +114,8 @@ * nodes. */ @Override - public void print(Graph graph, String title, Map properties) { + public void print(Graph graph, Map properties, int id, String format, Object... args) { + String title = id + ": " + String.format(format, args); beginGraph(title); EconomicSet noBlockNodes = EconomicSet.create(Equivalence.IDENTITY); ScheduleResult schedule = null; diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/NoDeadCodeVerifyHandler.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/NoDeadCodeVerifyHandler.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/NoDeadCodeVerifyHandler.java Thu Jun 01 02:21:18 2017 +0000 @@ -63,7 +63,7 @@ private static final Map discovered = new ConcurrentHashMap<>(); @Override - public void verify(Object object, String message) { + public void verify(Object object, String format, Object... args) { OptionValues options = DebugScope.getConfig().getOptions(); if (Options.NDCV.getValue(options) != OFF && object instanceof StructuredGraph) { StructuredGraph graph = (StructuredGraph) object; @@ -72,9 +72,9 @@ List after = graph.getNodes().snapshot(); assert after.size() <= before.size(); if (before.size() != after.size()) { - if (discovered.put(message, Boolean.TRUE) == null) { + if (discovered.put(format, Boolean.TRUE) == null) { before.removeAll(after); - String prefix = message == null ? "" : message + ": "; + String prefix = format == null ? "" : format + ": "; GraalError error = new GraalError("%sfound dead nodes in %s: %s", prefix, graph, before); if (Options.NDCV.getValue(options) == INFO) { System.out.println(error.getMessage()); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,19 +22,33 @@ */ package org.graalvm.compiler.replacements.test; +import static org.graalvm.compiler.java.BytecodeParserOptions.InlinePartialIntrinsicExitDuringParsing; + import java.util.function.Function; +import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.api.replacements.ClassSubstitution; import org.graalvm.compiler.api.replacements.MethodSubstitution; import org.graalvm.compiler.bytecode.BytecodeProvider; +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.DebugConfigScope; +import org.graalvm.compiler.graph.GraalGraphError; import org.graalvm.compiler.graph.Node.ConstantNodeParameter; import org.graalvm.compiler.graph.Node.NodeIntrinsic; import org.graalvm.compiler.nodes.PiNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; +import org.graalvm.compiler.options.OptionValues; +import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; +import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.meta.ResolvedJavaMethod; /** @@ -42,6 +56,26 @@ */ public class ReplacementsParseTest extends ReplacementsTest { + private static final String IN_COMPILED_HANDLER_MARKER = "*** in compiled handler ***"; + + /** + * Marker value to indicate an exception handler was interpreted. We cannot use a complex string + * expression in this context without risking non-deterministic behavior dependent on whether + * String intrinsics are applied or whether String expression evaluation hit an uncommon trap + * when executed by C1 or C2 (and thus potentially altering the profile such that the exception + * handler is *not* compiled by Graal even when we want it to be). + */ + private static final String IN_INTERPRETED_HANDLER_MARKER = "*** in interpreted handler ***"; + + private InlineInvokePlugin.InlineInfo inlineInvokeDecision; + + @SuppressWarnings("serial") + static class CustomError extends Error { + CustomError(String message) { + super(message); + } + } + static final Object THROW_EXCEPTION_MARKER = new Object() { @Override public String toString() { @@ -49,6 +83,24 @@ } }; + static int copyFirstBody(byte[] left, byte[] right, boolean left2right) { + if (left2right) { + byte e = left[0]; + right[0] = e; + return e; + } else { + byte e = right[0]; + left[0] = e; + return e; + } + } + + static int copyFirstL2RBody(byte[] left, byte[] right) { + byte e = left[0]; + right[0] = e; + return e; + } + static class TestObject { static double next(double v) { return Math.nextAfter(v, 1.0); @@ -73,26 +125,40 @@ final Object id; String stringizeId() { - String res = String.valueOf(id); - if (res.equals(THROW_EXCEPTION_MARKER.toString())) { + Object res = id; + if (res == THROW_EXCEPTION_MARKER) { // Tests exception throwing from partial intrinsification - throw new RuntimeException("ex: " + id); + throw new CustomError("ex"); } - return res; + return String.valueOf(res); } static String stringize(Object obj) { - String res = String.valueOf(obj); - if (res.equals(THROW_EXCEPTION_MARKER.toString())) { + Object res = obj; + if (res == THROW_EXCEPTION_MARKER) { // Tests exception throwing from partial intrinsification - throw new RuntimeException("ex: " + obj); + throw new CustomError("ex"); } - return res; + return String.valueOf(res); } static String identity(String s) { return s; } + + /** + * @see TestObjectSubstitutions#copyFirst(byte[], byte[], boolean) + */ + static int copyFirst(byte[] left, byte[] right, boolean left2right) { + return copyFirstBody(left, right, left2right); + } + + /** + * @see TestObjectSubstitutions#copyFirstL2R(byte[], byte[]) + */ + static int copyFirstL2R(byte[] left, byte[] right) { + return copyFirstL2RBody(left, right); + } } @ClassSubstitution(TestObject.class) @@ -146,6 +212,25 @@ private static native String asNonNullStringIntrinsic(Object object, @ConstantNodeParameter Class toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull); /** + * An valid intrinsic as the frame state associated with the merge should prevent the frame + * states associated with the array stores from being associated with subsequent + * deoptimizing nodes. + */ + @MethodSubstitution + static int copyFirst(byte[] left, byte[] right, boolean left2right) { + return copyFirstBody(left, right, left2right); + } + + /** + * An invalid intrinsic as the frame state associated with the array assignment can leak out + * to subsequent deoptimizing nodes. + */ + @MethodSubstitution + static int copyFirstL2R(byte[] left, byte[] right) { + return copyFirstL2RBody(left, right); + } + + /** * Tests that non-capturing lambdas are folded away. */ @MethodSubstitution @@ -166,6 +251,8 @@ r.registerMethodSubstitution(TestObjectSubstitutions.class, "nextAfter", double.class, double.class); r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringize", Object.class); r.registerMethodSubstitution(TestObjectSubstitutions.class, "stringizeId", Receiver.class); + r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirst", byte[].class, byte[].class, boolean.class); + r.registerMethodSubstitution(TestObjectSubstitutions.class, "copyFirstL2R", byte[].class, byte[].class); if (replacementBytecodeProvider.supportsInvokedynamic()) { r.registerMethodSubstitution(TestObjectSubstitutions.class, "identity", String.class); @@ -173,6 +260,14 @@ super.registerInvocationPlugins(invocationPlugins); } + @BeforeClass + public static void warmupProfiles() { + for (int i = 0; i < 40000; i++) { + callCopyFirst(new byte[16], new byte[16], true); + callCopyFirstL2R(new byte[16], new byte[16]); + } + } + /** * Ensure that calling the original method from the substitution binds correctly. */ @@ -219,31 +314,100 @@ } } + private void testWithDifferentReturnValues(OptionValues options, String standardReturnValue, String compiledReturnValue, String name, Object... args) { + ResolvedJavaMethod method = getResolvedJavaMethod(name); + Object receiver = null; + + Result expect = executeExpected(method, receiver, args); + Assert.assertEquals(standardReturnValue, expect.returnValue); + expect = new Result(compiledReturnValue, null); + testAgainstExpected(options, method, expect, receiver, args); + } + + @Override + protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) { + return super.getCode(installedCodeOwner, graph, forceCompileOverride, installAsDefault, options); + } + + boolean forceCompileOverride; + @Test public void testCallStringize() { test("callStringize", "a string"); - test("callStringize", THROW_EXCEPTION_MARKER); test("callStringize", Boolean.TRUE); + // Unset 'exception seen' bit if testCallStringizeWithoutInlinePartialIntrinsicExit + // is executed before this test + getResolvedJavaMethod("callStringize").reprofile(); + forceCompileOverride = true; + String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER; + String compiledReturnValue = IN_COMPILED_HANDLER_MARKER; + testWithDifferentReturnValues(getInitialOptions(), standardReturnValue, compiledReturnValue, "callStringize", THROW_EXCEPTION_MARKER); + } + + @Test + public void testCallStringizeWithoutInlinePartialIntrinsicExit() { + OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false); + test(options, "callStringize", "a string"); + test(options, "callStringize", Boolean.TRUE); + String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER; + String compiledReturnValue = IN_COMPILED_HANDLER_MARKER; + for (int i = 0; i < 1000; i++) { + // Ensures 'exception seen' bit is set for call to stringize + callStringize(THROW_EXCEPTION_MARKER); + } + forceCompileOverride = true; + testWithDifferentReturnValues(options, standardReturnValue, compiledReturnValue, "callStringize", THROW_EXCEPTION_MARKER); } @Test public void testCallStringizeId() { test("callStringizeId", new TestObject("a string")); - test("callStringizeId", new TestObject(THROW_EXCEPTION_MARKER)); test("callStringizeId", new TestObject(Boolean.TRUE)); + // Unset 'exception seen' bit if testCallStringizeIdWithoutInlinePartialIntrinsicExit + // is executed before this test + getResolvedJavaMethod("callStringize").reprofile(); + forceCompileOverride = true; + String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER; + String compiledReturnValue = IN_COMPILED_HANDLER_MARKER; + testWithDifferentReturnValues(getInitialOptions(), standardReturnValue, compiledReturnValue, "callStringizeId", new TestObject(THROW_EXCEPTION_MARKER)); + } + + @Test + public void testCallStringizeIdWithoutInlinePartialIntrinsicExit() { + OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false); + test(options, "callStringizeId", new TestObject("a string")); + test(options, "callStringizeId", new TestObject(Boolean.TRUE)); + TestObject exceptionTestObject = new TestObject(THROW_EXCEPTION_MARKER); + for (int i = 0; i < 1000; i++) { + // Ensures 'exception seen' bit is set for call to stringizeId + callStringizeId(exceptionTestObject); + } + String standardReturnValue = IN_INTERPRETED_HANDLER_MARKER; + String compiledReturnValue = IN_COMPILED_HANDLER_MARKER; + forceCompileOverride = true; + testWithDifferentReturnValues(options, standardReturnValue, compiledReturnValue, "callStringizeId", exceptionTestObject); } public static Object callStringize(Object obj) { - return TestObject.stringize(obj); + try { + return TestObject.stringize(obj); + } catch (CustomError e) { + if (GraalDirectives.inCompiledCode()) { + return IN_COMPILED_HANDLER_MARKER; + } + return IN_INTERPRETED_HANDLER_MARKER; + } } public static Object callStringizeId(TestObject testObj) { - return indirect(testObj); - } - - @BytecodeParserNeverInline - private static String indirect(TestObject testObj) { - return testObj.stringizeId(); + try { + return testObj.stringizeId(); + } catch (CustomError e) { + if (GraalDirectives.inCompiledCode()) { + return IN_COMPILED_HANDLER_MARKER; + } + return IN_INTERPRETED_HANDLER_MARKER; + } } @Test @@ -263,4 +427,67 @@ public static String callLambda(String value) { return TestObject.identity(value); } + + public static int callCopyFirst(byte[] in, byte[] out, boolean left2right) { + int res = TestObject.copyFirst(in, out, left2right); + if (res == 17) { + // A node after the intrinsic that needs a frame state. + GraalDirectives.deoptimize(); + } + return res; + } + + public static int callCopyFirstWrapper(byte[] in, byte[] out, boolean left2right) { + return callCopyFirst(in, out, left2right); + } + + public static int callCopyFirstL2R(byte[] in, byte[] out) { + int res = TestObject.copyFirstL2R(in, out); + if (res == 17) { + // A node after the intrinsic that needs a frame state. + GraalDirectives.deoptimize(); + } + return res; + } + + @Test + public void testCallCopyFirst() { + byte[] in = {0, 1, 2, 3, 4}; + byte[] out = new byte[in.length]; + test("callCopyFirst", in, out, true); + test("callCopyFirst", in, out, false); + } + + @SuppressWarnings("try") + @Test + public void testCallCopyFirstL2R() { + byte[] in = {0, 1, 2, 3, 4}; + byte[] out = new byte[in.length]; + try { + try (DebugConfigScope s = Debug.setConfig(Debug.silentConfig())) { + test("callCopyFirstL2R", in, out); + } + } catch (GraalGraphError e) { + assertTrue(e.getMessage().startsWith("Invalid frame state")); + } + } + + @Override + protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { + return inlineInvokeDecision; + } + + @Test + public void testCallCopyFirstWithoutInlinePartialIntrinsicExit() { + OptionValues options = new OptionValues(getInitialOptions(), InlinePartialIntrinsicExitDuringParsing, false); + inlineInvokeDecision = InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION; + try { + byte[] in = {0, 1, 2, 3, 4}; + byte[] out = new byte[in.length]; + test(options, "callCopyFirstWrapper", in, out, true); + test(options, "callCopyFirstWrapper", in, out, false); + } finally { + inlineInvokeDecision = null; + } + } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java Thu Jun 01 02:21:18 2017 +0000 @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; +import org.graalvm.api.word.LocationIdentity; import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.StampPair; @@ -45,6 +46,8 @@ import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.InvokeNode; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; +import org.graalvm.compiler.nodes.KillingBeginNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -54,6 +57,7 @@ import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool; import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; +import org.graalvm.compiler.nodes.java.ExceptionObjectNode; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.spi.StampProvider; import org.graalvm.compiler.nodes.type.StampTool; @@ -364,7 +368,7 @@ pushStructure(s); } - private IfStructure saveLastNode() { + private IfStructure saveLastIfNode() { IfStructure s = getTopStructure(IfStructure.class); switch (s.state) { case CONDITION: @@ -385,19 +389,19 @@ } public void thenPart() { - IfStructure s = saveLastNode(); + IfStructure s = saveLastIfNode(); lastFixedNode = (FixedWithNextNode) s.thenPart; s.state = IfState.THEN_PART; } public void elsePart() { - IfStructure s = saveLastNode(); + IfStructure s = saveLastIfNode(); lastFixedNode = (FixedWithNextNode) s.elsePart; s.state = IfState.ELSE_PART; } public void endIf() { - IfStructure s = saveLastNode(); + IfStructure s = saveLastIfNode(); FixedWithNextNode thenPart = s.thenPart instanceof FixedWithNextNode ? (FixedWithNextNode) s.thenPart : null; FixedWithNextNode elsePart = s.elsePart instanceof FixedWithNextNode ? (FixedWithNextNode) s.elsePart : null; @@ -430,4 +434,131 @@ s.state = IfState.FINISHED; popStructure(); } + + static class InvokeWithExceptionStructure extends Structure { + protected enum State { + INVOKE, + NO_EXCEPTION_EDGE, + EXCEPTION_EDGE, + FINISHED + } + + protected State state; + protected ExceptionObjectNode exceptionObject; + protected FixedNode noExceptionEdge; + protected FixedNode exceptionEdge; + } + + public InvokeWithExceptionNode startInvokeWithException(ResolvedJavaMethod method, InvokeKind invokeKind, + FrameStateBuilder frameStateBuilder, int invokeBci, int exceptionEdgeBci, ValueNode... args) { + + assert method.isStatic() == (invokeKind == InvokeKind.Static); + Signature signature = method.getSignature(); + JavaType returnType = signature.getReturnType(null); + assert checkArgs(method, args); + StampPair returnStamp = graphBuilderPlugins.getOverridingStamp(this, returnType, false); + if (returnStamp == null) { + returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false); + } + ExceptionObjectNode exceptionObject = add(new ExceptionObjectNode(getMetaAccess())); + if (frameStateBuilder != null) { + FrameStateBuilder exceptionState = frameStateBuilder.copy(); + exceptionState.clearStack(); + exceptionState.push(JavaKind.Object, exceptionObject); + exceptionState.setRethrowException(false); + exceptionObject.setStateAfter(exceptionState.create(exceptionEdgeBci, exceptionObject)); + } + MethodCallTargetNode callTarget = graph.add(createMethodCallTarget(invokeKind, method, args, returnStamp, invokeBci)); + InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionObject, invokeBci)); + AbstractBeginNode noExceptionEdge = graph.add(KillingBeginNode.create(LocationIdentity.any())); + invoke.setNext(noExceptionEdge); + if (frameStateBuilder != null) { + if (invoke.getStackKind() != JavaKind.Void) { + frameStateBuilder.push(returnType.getJavaKind(), invoke); + } + invoke.setStateAfter(frameStateBuilder.create(invokeBci, invoke)); + if (invoke.getStackKind() != JavaKind.Void) { + frameStateBuilder.pop(returnType.getJavaKind()); + } + } + lastFixedNode = null; + + InvokeWithExceptionStructure s = new InvokeWithExceptionStructure(); + s.state = InvokeWithExceptionStructure.State.INVOKE; + s.noExceptionEdge = noExceptionEdge; + s.exceptionEdge = exceptionObject; + s.exceptionObject = exceptionObject; + pushStructure(s); + + return invoke; + } + + private InvokeWithExceptionStructure saveLastInvokeWithExceptionNode() { + InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class); + switch (s.state) { + case INVOKE: + assert lastFixedNode == null; + break; + case NO_EXCEPTION_EDGE: + s.noExceptionEdge = lastFixedNode; + break; + case EXCEPTION_EDGE: + s.exceptionEdge = lastFixedNode; + break; + case FINISHED: + assert false; + break; + } + lastFixedNode = null; + return s; + } + + public void noExceptionPart() { + InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); + lastFixedNode = (FixedWithNextNode) s.noExceptionEdge; + s.state = InvokeWithExceptionStructure.State.NO_EXCEPTION_EDGE; + } + + public void exceptionPart() { + InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); + lastFixedNode = (FixedWithNextNode) s.exceptionEdge; + s.state = InvokeWithExceptionStructure.State.EXCEPTION_EDGE; + } + + public ExceptionObjectNode exceptionObject() { + InvokeWithExceptionStructure s = getTopStructure(InvokeWithExceptionStructure.class); + return s.exceptionObject; + } + + /** + * Finishes a control flow started with {@link #startInvokeWithException}. If necessary, creates + * a merge of the non-exception and exception edges. The merge node is returned and the + * non-exception edge is the first forward end of the merge, the exception edge is the second + * forward end (relevant for phi nodes). + */ + public AbstractMergeNode endInvokeWithException() { + InvokeWithExceptionStructure s = saveLastInvokeWithExceptionNode(); + FixedWithNextNode noExceptionEdge = s.noExceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.noExceptionEdge : null; + FixedWithNextNode exceptionEdge = s.exceptionEdge instanceof FixedWithNextNode ? (FixedWithNextNode) s.exceptionEdge : null; + AbstractMergeNode merge = null; + if (noExceptionEdge != null && exceptionEdge != null) { + EndNode noExceptionEnd = graph.add(new EndNode()); + graph.addAfterFixed(noExceptionEdge, noExceptionEnd); + EndNode exceptionEnd = graph.add(new EndNode()); + graph.addAfterFixed(exceptionEdge, exceptionEnd); + merge = graph.add(new MergeNode()); + merge.addForwardEnd(noExceptionEnd); + merge.addForwardEnd(exceptionEnd); + lastFixedNode = merge; + } else if (noExceptionEdge != null) { + lastFixedNode = noExceptionEdge; + } else if (exceptionEdge != null) { + lastFixedNode = exceptionEdge; + } else { + assert lastFixedNode == null; + } + s.state = InvokeWithExceptionStructure.State.FINISHED; + popStructure(); + return merge; + } } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java Thu Jun 01 02:21:18 2017 +0000 @@ -521,7 +521,9 @@ * anything because the usages of the frameState are not available yet. So we need * to call it again. */ - InliningUtil.handleMissingAfterExceptionFrameState(frameState); + PEMethodScope peMethodScope = (PEMethodScope) methodScope; + Invoke invoke = peMethodScope.invokeData != null ? peMethodScope.invokeData.invoke : null; + InliningUtil.handleMissingAfterExceptionFrameState(frameState, invoke, null, true); /* * The frameState must be gone now, because it is not a valid deoptimization point. @@ -1168,8 +1170,8 @@ */ invokeArgsList = Arrays.asList(methodScope.arguments); } - return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, methodScope.method, methodScope.exceptionState, methodScope.outerState, true, methodScope.method, - invokeArgsList); + return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, null, methodScope.method, methodScope.exceptionState, methodScope.outerState, true, + methodScope.method, invokeArgsList); } else if (node instanceof MonitorIdNode) { ensureOuterStateDecoded(methodScope); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -52,6 +52,7 @@ import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; +import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; @@ -85,7 +86,7 @@ public BasicArrayCopyNode(NodeClass type, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, JavaKind elementKind) { super(type, StampFactory.forKind(JavaKind.Void)); - this.bci = -6; + this.bci = BytecodeFrame.INVALID_FRAMESTATE_BCI; args = new NodeInputList<>(this, new ValueNode[]{src, srcPos, dest, destPos, length}); this.elementKind = elementKind != JavaKind.Illegal ? elementKind : null; } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java Thu Jun 01 02:21:18 2017 +0000 @@ -64,7 +64,7 @@ return updateStamp(computeStamp(getObject())); } - private static Stamp computeStamp(ValueNode object) { + protected Stamp computeStamp(ValueNode object) { Stamp objectStamp = object.stamp(); if (objectStamp instanceof ObjectStamp) { objectStamp = objectStamp.join(StampFactory.objectNonNull()); @@ -82,15 +82,17 @@ * * If yes, then the exact type is returned, otherwise it returns null. */ - protected static ResolvedJavaType getConcreteType(Stamp stamp) { - if (!(stamp instanceof ObjectStamp)) { + protected ResolvedJavaType getConcreteType(Stamp forStamp) { + if (!(forStamp instanceof ObjectStamp)) { return null; } - ObjectStamp objectStamp = (ObjectStamp) stamp; + ObjectStamp objectStamp = (ObjectStamp) forStamp; if (objectStamp.type() == null) { return null; } else if (objectStamp.isExactType()) { return objectStamp.type().isCloneableWithAllocation() ? objectStamp.type() : null; + } else if (objectStamp.type().isArray()) { + return objectStamp.type(); } return null; } diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java Thu Jun 01 02:21:18 2017 +0000 @@ -22,6 +22,8 @@ */ package org.graalvm.compiler.test; +import org.graalvm.util.CollectionsUtil; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -31,11 +33,10 @@ import java.util.Arrays; import java.util.Formatter; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.graalvm.util.CollectionsUtil; - /** * Utility methods for spawning a VM in a subprocess during unit tests. */ @@ -184,9 +185,46 @@ * @param mainClassAndArgs the main class and its arguments */ public static Subprocess java(List vmArgs, List mainClassAndArgs) throws IOException, InterruptedException { + return javaHelper(vmArgs, null, mainClassAndArgs); + } + + /** + * Executes a Java subprocess. + * + * @param vmArgs the VM arguments + * @param env the environment variables + * @param mainClassAndArgs the main class and its arguments + */ + public static Subprocess java(List vmArgs, Map env, String... mainClassAndArgs) throws IOException, InterruptedException { + return java(vmArgs, env, Arrays.asList(mainClassAndArgs)); + } + + /** + * Executes a Java subprocess. + * + * @param vmArgs the VM arguments + * @param env the environment variables + * @param mainClassAndArgs the main class and its arguments + */ + public static Subprocess java(List vmArgs, Map env, List mainClassAndArgs) throws IOException, InterruptedException { + return javaHelper(vmArgs, env, mainClassAndArgs); + } + + /** + * Executes a Java subprocess. + * + * @param vmArgs the VM arguments + * @param env the environment variables + * @param mainClassAndArgs the main class and its arguments + */ + private static Subprocess javaHelper(List vmArgs, Map env, List mainClassAndArgs) throws IOException, InterruptedException { List command = new ArrayList<>(vmArgs); command.addAll(mainClassAndArgs); ProcessBuilder processBuilder = new ProcessBuilder(command); + if (env != null) { + Map processBuilderEnv = processBuilder.environment(); + processBuilderEnv.putAll(env); + } processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream())); diff -r 8cc204b35e39 -r 4a5b7a2dd783 hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java --- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java Wed May 31 09:19:28 2017 -0400 +++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java Thu Jun 01 02:21:18 2017 +0000 @@ -26,7 +26,7 @@ import java.util.BitSet; import java.util.Iterator; import java.util.List; -import java.util.function.IntFunction; +import java.util.function.IntUnaryOperator; import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.cfg.Loop; @@ -806,7 +806,7 @@ private boolean mergeObjectStates(int resultObject, int[] sourceObjects, PartialEscapeBlockState[] states) { boolean compatible = true; boolean ensureVirtual = true; - IntFunction getObject = index -> sourceObjects == null ? resultObject : sourceObjects[index]; + IntUnaryOperator getObject = index -> sourceObjects == null ? resultObject : sourceObjects[index]; VirtualObjectNode virtual = virtualObjects.get(resultObject); int entryCount = virtual.entryCount(); @@ -814,7 +814,7 @@ // determine all entries that have a two-slot value JavaKind[] twoSlotKinds = null; outer: for (int i = 0; i < states.length; i++) { - ObjectState objectState = states[i].getObjectState(getObject.apply(i)); + ObjectState objectState = states[i].getObjectState(getObject.applyAsInt(i)); ValueNode[] entries = objectState.getEntries(); int valueIndex = 0; ensureVirtual &= objectState.getEnsureVirtualized(); @@ -845,7 +845,7 @@ if (twoSlotKinds[valueIndex] != null) { assert valueIndex < virtual.entryCount() - 1 && virtual.entryKind(valueIndex) == JavaKind.Int && virtual.entryKind(valueIndex + 1) == JavaKind.Int; for (int i = 0; i < states.length; i++) { - int object = getObject.apply(i); + int object = getObject.applyAsInt(i); ObjectState objectState = states[i].getObjectState(object); ValueNode value = objectState.getEntry(valueIndex); JavaKind valueKind = value.getStackKind(); @@ -867,13 +867,13 @@ if (compatible) { // virtual objects are compatible: create phis for all entries that need them - ValueNode[] values = states[0].getObjectState(getObject.apply(0)).getEntries().clone(); + ValueNode[] values = states[0].getObjectState(getObject.applyAsInt(0)).getEntries().clone(); PhiNode[] phis = getValuePhis(virtual, virtual.entryCount()); int valueIndex = 0; while (valueIndex < values.length) { for (int i = 1; i < states.length; i++) { if (phis[valueIndex] == null) { - ValueNode field = states[i].getObjectState(getObject.apply(i)).getEntry(valueIndex); + ValueNode field = states[i].getObjectState(getObject.applyAsInt(i)).getEntry(valueIndex); if (values[valueIndex] != field) { phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted()); } @@ -900,7 +900,7 @@ materialized |= mergeObjectEntry(getObject, states, phi, i); } else { for (int i2 = 0; i2 < states.length; i2++) { - ObjectState state = states[i2].getObjectState(getObject.apply(i2)); + ObjectState state = states[i2].getObjectState(getObject.applyAsInt(i2)); if (!state.isVirtual()) { break; } @@ -910,19 +910,19 @@ values[i] = phi; } } - newState.addObject(resultObject, new ObjectState(values, states[0].getObjectState(getObject.apply(0)).getLocks(), ensureVirtual)); + newState.addObject(resultObject, new ObjectState(values, states[0].getObjectState(getObject.applyAsInt(0)).getLocks(), ensureVirtual)); return materialized; } else { // not compatible: materialize in all predecessors PhiNode materializedValuePhi = getPhi(resultObject, StampFactory.forKind(JavaKind.Object)); for (int i = 0; i < states.length; i++) { Block predecessor = getPredecessor(i); - if (!ensureVirtual && states[i].getObjectState(virtual).isVirtual()) { + if (!ensureVirtual && states[i].getObjectState(getObject.applyAsInt(i)).isVirtual()) { // we can materialize if not all inputs are "ensureVirtualized" - states[i].getObjectState(virtual).setEnsureVirtualized(false); + states[i].getObjectState(getObject.applyAsInt(i)).setEnsureVirtualized(false); } - ensureMaterialized(states[i], getObject.apply(i), predecessor.getEndNode(), blockEffects.get(predecessor), COUNTER_MATERIALIZATIONS_MERGE); - setPhiInput(materializedValuePhi, i, states[i].getObjectState(getObject.apply(i)).getMaterializedValue()); + ensureMaterialized(states[i], getObject.applyAsInt(i), predecessor.getEndNode(), blockEffects.get(predecessor), COUNTER_MATERIALIZATIONS_MERGE); + setPhiInput(materializedValuePhi, i, states[i].getObjectState(getObject.applyAsInt(i)).getMaterializedValue()); } newState.addObject(resultObject, new ObjectState(materializedValuePhi, null, ensureVirtual)); return true; @@ -935,10 +935,10 @@ * * @return true if materialization happened during the merge, false otherwise */ - private boolean mergeObjectEntry(IntFunction objectIdFunc, PartialEscapeBlockState[] states, PhiNode phi, int entryIndex) { + private boolean mergeObjectEntry(IntUnaryOperator objectIdFunc, PartialEscapeBlockState[] states, PhiNode phi, int entryIndex) { boolean materialized = false; for (int i = 0; i < states.length; i++) { - int object = objectIdFunc.apply(i); + int object = objectIdFunc.applyAsInt(i); ObjectState objectState = states[i].getObjectState(object); if (!objectState.isVirtual()) { break;