8181369: Update Graal

Reviewed-by: kvn
This commit is contained in:
Igor Veresov 2017-05-31 18:20:20 -07:00
parent 292ac89940
commit 993168dfd8
64 changed files with 1987 additions and 1421 deletions

View File

@ -113,12 +113,7 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
@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 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
}
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 class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
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 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
@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 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
@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 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
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 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
}
}
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 @@ public class SPARCArithmeticLIRGenerator extends ArithmeticLIRGenerator {
}
}
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;

View File

@ -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 @@ public class HashCodeTest extends GraalCompilerTest {
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());
}

View File

@ -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);
}
}

View File

@ -770,7 +770,7 @@ public class Debug {
* @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 @@ public class Debug {
* @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()) {

View File

@ -26,7 +26,7 @@ import java.io.Closeable;
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 @@ public interface DebugDumpHandler extends Closeable {
/**
* 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();

View File

@ -70,7 +70,7 @@ public class DebugEnvironment {
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<DebugDumpHandler> dumpHandlers = new ArrayList<>();

View File

@ -31,7 +31,8 @@ public interface DebugVerifyHandler {
* 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);
}

View File

@ -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<Boolean> UseFingerprinting = new OptionKey<>(false);
@Option(help = "Limit number of events shown in fingerprinting error message.")//
public static final OptionKey<Integer> FingerprintErrorEventTailLength = new OptionKey<>(50);
@Option(help = "Fingerprinting event at which to execute breakpointable code.")//
public static final OptionKey<Integer> FingerprintingBreakpointEvent = new OptionKey<>(-1);
}
/**
* Determines whether fingerprinting is enabled.
*/
public static final boolean ENABLED = Options.UseFingerprinting.getValue(DEBUG_OPTIONS);
private static final ThreadLocal<Fingerprint> current = ENABLED ? new ThreadLocal<>() : null;
private final List<String> 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<String> 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<String> 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++;
}
}
}

View File

@ -333,9 +333,8 @@ public final class DebugScope implements Debug.Scope {
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 final class DebugScope implements Debug.Scope {
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 @@ public final class DebugScope implements Debug.Scope {
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);
}
}
}

View File

@ -161,7 +161,8 @@
</module>
</module>
<module name="RegexpHeader">
<property name="header" value="/\*\n \* Copyright \(c\) (20[0-9][0-9], )?20[0-9][0-9], Oracle and/or its affiliates. All rights reserved.\n \* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n \*\n \* This code is free software; you can redistribute it and/or modify it\n \* under the terms of the GNU General Public License version 2 only, as\n \* published by the Free Software Foundation.\n \*\n \* This code is distributed in the hope that it will be useful, but WITHOUT\n \* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n \* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n \* version 2 for more details \(a copy is included in the LICENSE file that\n \* accompanied this code\).\n \*\n \* You should have received a copy of the GNU General Public License version\n \* 2 along with this work; if not, write to the Free Software Foundation,\n \* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n \*\n \* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n \* or visit www.oracle.com if you need additional information or have any\n \* questions.\n \*/\n"/>
<property name="header" value="/\*\n \* Copyright \(c\) (20[0-9][0-9], )?20[0-9][0-9], Oracle and/or its affiliates\. All rights reserved\.\n \* Copyright \(c\) (20[0-9][0-9], )?20[0-9][0-9], .*\. All rights reserved\.\n \* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER\.\n \*\n \* This code is free software; you can redistribute it and/or modify it\n \* under the terms of the GNU General Public License version 2 only, as\n \* published by the Free Software Foundation\.\n \*\n \* This code is distributed in the hope that it will be useful, but WITHOUT\n \* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n \* FITNESS FOR A PARTICULAR PURPOSE\. See the GNU General Public License\n \* version 2 for more details \(a copy is included in the LICENSE file that\n \* accompanied this code\)\.\n \*\n \* You should have received a copy of the GNU General Public License version\n \* 2 along with this work; if not, write to the Free Software Foundation,\n \* Inc\., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\.\n \*\n \* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n \* or visit www\.oracle\.com if you need additional information or have any\n \* questions\.\n \*/\n"/>
<property name="multiLines" value="3"/>
<property name="fileExtensions" value="java"/>
</module>
<module name="FileTabCharacter">

View File

@ -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.EconomicMap;
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 @@ public class Graph {
if (nodeEventListener != null) {
nodeEventListener.nodeAdded(node);
}
if (Fingerprint.ENABLED) {
Fingerprint.submit("%s: %s", NodeEvent.NODE_ADDED, node);
}
afterRegister(node);
}

View File

@ -43,8 +43,6 @@ import org.graalvm.compiler.core.common.Fields;
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 @@ public abstract class Node implements Cloneable, Formattable, NodeInterface {
if (listener != null) {
listener.inputChanged(node);
}
if (Fingerprint.ENABLED) {
Fingerprint.submit("%s: %s", NodeEvent.INPUT_CHANGED, node);
}
}
}
@ -751,9 +746,6 @@ public abstract class Node implements Cloneable, Formattable, NodeInterface {
if (listener != null && node.isAlive()) {
listener.usagesDroppedToZero(node);
}
if (Fingerprint.ENABLED) {
Fingerprint.submit("%s: %s", NodeEvent.ZERO_USAGES, node);
}
}
}

View File

@ -49,7 +49,6 @@ 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.Edges.Type;
import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
@ -810,10 +809,11 @@ public final class NodeClass<T> extends FieldIntrospection<T> {
/**
* The template used to build the {@link Verbosity#Name} version. Variable parts are specified
* using &#123;i#inputName&#125; or &#123;p#propertyName&#125;.
* using &#123;i#inputName&#125; or &#123;p#propertyName&#125;. 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 @@ public final class NodeClass<T> extends FieldIntrospection<T> {
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();

View File

@ -24,17 +24,21 @@ package org.graalvm.compiler.hotspot.test;
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 @@ public class HotSpotGraalMBeanTest {
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);
}
}

View File

@ -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<Object> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
list.add(i);
}
test("cloneList", list);
}
}

View File

@ -51,7 +51,7 @@ public class OptionsInFileTest extends GraalCompilerTest {
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 @@ public class OptionsInFileTest extends GraalCompilerTest {
}
List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
vmArgs.removeIf(a -> a.startsWith("-Dgraal."));
vmArgs.add("-Dgraal.options.file=" + optionsFile);
vmArgs.add("-XX:+JVMCIPrintProperties");
Subprocess proc = SubprocessUtil.java(vmArgs);

View File

@ -63,6 +63,9 @@ public class RetryableCompilationTest extends GraalCompilerTest {
private static void testHelper(List<String> extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException {
List<String> 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);

View File

@ -24,8 +24,14 @@ package org.graalvm.compiler.hotspot;
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.StackLockValue;
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 @@ public class HotSpotDebugInfoBuilder extends DebugInfoBuilder {
@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<String> 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;
}
}

View File

@ -25,14 +25,12 @@ package org.graalvm.compiler.hotspot;
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.OptionKey;
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<OptionKey<?>, Object> changes;
private ObjectName registered;
private OptionValues cachedOptions;
private HotSpotGraalMBean(OptionValues options) {
this.options = options;
@ -112,27 +112,51 @@ public final class HotSpotGraalMBean implements DynamicMBean {
}
@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<OptionKey<?>, 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 @@ public final class HotSpotGraalMBean implements DynamicMBean {
@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 final class HotSpotGraalMBean implements DynamicMBean {
public MBeanInfo getMBeanInfo() {
List<MBeanAttributeInfo> attrs = new ArrayList<>();
if (registered != null) {
for (Iterator<OptionDescriptors> 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 @@ public final class HotSpotGraalMBean implements DynamicMBean {
null, null, null);
}
private static Iterable<OptionDescriptor> allOptionDescriptors() {
List<OptionDescriptor> arr = new ArrayList<>();
for (OptionDescriptors set : OptionsParser.getOptionsLoader()) {
for (OptionDescriptor descr : set) {
arr.add(descr);
}
}
return arr;
}
}

View File

@ -88,7 +88,6 @@ import org.graalvm.compiler.hotspot.nodes.DimensionsNode;
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 class NewObjectSnippets implements Snippets {
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 @@ public class NewObjectSnippets implements Snippets {
@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)

View File

@ -24,6 +24,8 @@ package org.graalvm.compiler.hotspot.replacements;
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;
@ -59,6 +61,18 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu
super(TYPE, invokeKind, targetMethod, bci, returnStamp, receiver);
}
@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) {
@ -77,6 +91,7 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu
}
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 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu
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;
}
}

View File

@ -248,6 +248,7 @@ import static org.graalvm.compiler.core.common.type.StampFactory.objectNonNull;
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.Formatter;
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 @@ public class BytecodeParser implements GraphBuilderContext {
FrameState stateBefore;
final Mark mark;
final BytecodeParser parser;
List<ReturnToCallerData> returnDataList;
/**
* Creates a scope for root parsing an intrinsic.
@ -504,46 +507,50 @@ public class BytecodeParser implements GraphBuilderContext {
* 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.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);
}
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();
// 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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
this.returnValue = returnValue;
this.beforeReturnNode = beforeReturnNode;
}
static boolean containsReturnValue(List<ReturnToCallerData> list, ValueNode value) {
for (ReturnToCallerData e : list) {
if (e.returnValue == value) {
return true;
}
}
return false;
}
}
private final GraphBuilderPhase.Instance graphBuilderInstance;
@ -1333,8 +1367,19 @@ public class BytecodeParser implements GraphBuilderContext {
}
}
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 @@ public class BytecodeParser implements GraphBuilderContext {
@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 @@ public class BytecodeParser implements GraphBuilderContext {
@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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
}
}
} 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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
}
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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
/**
* 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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
}
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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
/**
* 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 @@ public class BytecodeParser implements GraphBuilderContext {
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 @@ public class BytecodeParser implements GraphBuilderContext {
} 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 @@ public class BytecodeParser implements GraphBuilderContext {
}
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 @@ public class BytecodeParser implements GraphBuilderContext {
}
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 @@ public class BytecodeParser implements GraphBuilderContext {
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--) {

View File

@ -40,6 +40,12 @@ public class BytecodeParserOptions {
@Option(help = "Inlines trivial methods during bytecode parsing.", type = OptionType.Expert)
public static final OptionKey<Boolean> 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<Boolean> InlinePartialIntrinsicExitDuringParsing = new OptionKey<>(true);
@Option(help = "Inlines intrinsic methods during bytecode parsing.", type = OptionType.Expert)
public static final OptionKey<Boolean> InlineIntrinsicsDuringParsing = new OptionKey<>(true);

View File

@ -42,8 +42,8 @@ import java.util.function.Function;
import org.graalvm.compiler.bytecode.Bytecode;
import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
import org.graalvm.compiler.core.common.PermanentBailoutException;
import org.graalvm.compiler.core.common.GraalOptions;
import org.graalvm.compiler.core.common.PermanentBailoutException;
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 final class FrameStateBuilder implements SideEffectsState {
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 final class FrameStateBuilder implements SideEffectsState {
}
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 @@ public final class FrameStateBuilder implements SideEffectsState {
// 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);

View File

@ -170,7 +170,9 @@ public class SPARCArithmetic {
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 @@ public class SPARCArithmetic {
}
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 @@ public class SPARCArithmetic {
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);

View File

@ -35,56 +35,6 @@ import jdk.vm.ci.meta.Value;
*/
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.
*/

View File

@ -24,6 +24,7 @@ package org.graalvm.compiler.lir.alloc.trace.lsra;
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.InstructionValueConsumer;
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 @@ final class RegisterVerifier {
BlockMap<TraceInterval[]> 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 @@ final class RegisterVerifier {
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 @@ final class RegisterVerifier {
public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> 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);
}

View File

@ -57,102 +57,6 @@ import jdk.vm.ci.meta.ValueKind;
*/
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 @@ final class TraceInterval extends IntervalHint {
* 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 @@ final class TraceInterval extends IntervalHint {
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);
private final OptionValues options;
TraceInterval(Variable operand) {
this(operand, operand.index);
}
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 @@ final class TraceInterval extends IntervalHint {
return location;
}
public ValueKind<?> kind() {
private ValueKind<?> kind() {
return operand.getValueKind();
}
@ -550,42 +475,14 @@ final class TraceInterval extends IntervalHint {
return i2.from();
}
/**
* 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 @@ final class TraceInterval extends IntervalHint {
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 @@ final class TraceInterval extends IntervalHint {
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 @@ final class TraceInterval extends IntervalHint {
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 @@ final class TraceInterval extends IntervalHint {
// 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 @@ final class TraceInterval extends IntervalHint {
}
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 @@ final class TraceInterval extends IntervalHint {
}
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 @@ final class TraceInterval extends IntervalHint {
}
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 @@ final class TraceInterval extends IntervalHint {
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 @@ final class TraceInterval extends IntervalHint {
* @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 @@ final class TraceInterval extends IntervalHint {
// 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 @@ final class TraceInterval extends IntervalHint {
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
*

View File

@ -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());
}
}
}

View File

@ -24,6 +24,7 @@ package org.graalvm.compiler.lir.alloc.trace.lsra;
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 @@ final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocatio
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 @@ final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocatio
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 @@ final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocatio
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 @@ final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocatio
// 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

View File

@ -24,6 +24,7 @@ package org.graalvm.compiler.lir.alloc.trace.lsra;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
@ -121,11 +122,10 @@ final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAlloca
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 @@ final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAlloca
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 @@ final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAlloca
*/
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();
}

View File

@ -191,11 +191,11 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
};
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 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
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 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
}
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 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
}
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 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
}
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 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
interval.setFrom(tempPos);
}
interval.addUsePos(tempPos, registerPriority);
interval.addUsePos(tempPos, registerPriority, allocator.getOptions());
interval.addMaterializationValue(null);
if (Debug.isLogEnabled()) {
@ -404,9 +404,9 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
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 @@ public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanA
if (isRegister(from)) {
return allocator.getOrCreateFixedInterval(asRegisterValue(from));
}
return allocator.getOrCreateInterval(from);
return allocator.getOrCreateInterval(asVariable(from));
}
}

View File

@ -24,7 +24,6 @@ package org.graalvm.compiler.lir.alloc.trace.lsra;
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 static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
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.GraalError;
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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<TraceInterval> SORT_BY_FROM_COMP = new Comparator<TraceInterval>() {
@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<TraceInterval> SORT_BY_SPILL_POS_COMP = new Comparator<TraceInterval>() {
@Override
public int compare(TraceInterval a, TraceInterval b) {
return a.spillDefinitionPos() - b.spillDefinitionPos();
}
};
@ -185,7 +180,9 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
private static <T extends IntervalHint> 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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
return true;
}
private static <T extends IntervalHint> 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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
*/
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
}
/**
* 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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
return registerAttributes[reg.number];
}
public boolean isAllocatable(RegisterValue register) {
return attributes(register.getRegister()).isAllocatable();
}
public MoveFactory getSpillMoveFactory() {
return moveFactory;
}
@ -331,20 +323,20 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
/**
* 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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
}
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
}
}
class CheckConsumer implements ValueConsumer {
boolean ok;
FixedInterval curInterval;
@Override
public void visitValue(Value operand, OperandMode mode, EnumSet<OperandFlag> 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<LIRInstruction> 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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
* @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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
}
}
TraceInterval intervalFor(Value operand) {
TraceInterval intervalFor(Variable operand) {
return intervalFor(operandNumber(operand));
}
@ -985,7 +896,7 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
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 @@ public final class TraceLinearScanPhase extends TraceAllocationPhase<TraceAlloca
}
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());
}
}

View File

@ -25,6 +25,7 @@ package org.graalvm.compiler.lir.alloc.trace.lsra;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
import static org.graalvm.compiler.lir.LIRValueUtil.asConstant;
import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
@ -190,13 +191,13 @@ final class TraceLinearScanResolveDataFlowPhase extends TraceLinearScanAllocatio
// 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);
}
}

View File

@ -39,12 +39,14 @@ import org.graalvm.compiler.debug.Debug;
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 @@ import jdk.vm.ci.meta.Value;
/**
*/
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
private void spillExcludeActiveFixed() {
FixedInterval interval = activeFixedList.getFixed();
FixedInterval interval = activeFixedList;
while (interval != FixedInterval.EndMarker) {
excludeFromUse(interval);
interval = interval.next;
@ -229,7 +365,7 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
}
// 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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
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 @@ final class TraceLinearScanWalker extends TraceIntervalWalker {
// 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());
}
}
}

View File

@ -323,11 +323,11 @@ final class TraceLocalMoveResolver {
}
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 @@ final class TraceLocalMoveResolver {
}
/**
* @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 @@ final class TraceLocalMoveResolver {
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 @@ final class TraceLocalMoveResolver {
// 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 @@ final class TraceLocalMoveResolver {
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 @@ final class TraceLocalMoveResolver {
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);

View File

@ -52,6 +52,7 @@ import org.graalvm.compiler.graph.iterators.NodeIterable;
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 @@ public final class FrameState extends VirtualState implements IterableNodeType {
/**
* @see BytecodeFrame#rethrowException
*/
protected boolean rethrowException;
protected final boolean rethrowException;
protected final boolean duringCall;
@ -184,15 +185,17 @@ public final class FrameState extends VirtualState implements IterableNodeType {
/**
* 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.<EscapeObjectState> 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.<EscapeObjectState> 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<MonitorIdNode> monitorIds,

View File

@ -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.NodeLIRBuilderTool;
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.

View File

@ -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.NodeLIRBuilderTool;
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 @@ public final class InvokeWithExceptionNode extends ControlSplitNode implements I
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 final class InvokeWithExceptionNode extends ControlSplitNode implements I
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;
}
}

View File

@ -96,7 +96,9 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable {
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;
}
}
}

View File

@ -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 @@ public class IntrinsicContext {
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 @@ public class IntrinsicContext {
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

View File

@ -709,14 +709,42 @@ public class InvocationPlugins {
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;
}
}
}

View File

@ -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.memory.MemoryCheckpoint;
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 @@ public final class ExceptionObjectNode extends BeginStateSplitNode implements Lo
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 @@ public final class ExceptionObjectNode extends BeginStateSplitNode implements Lo
@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();
}
}

View File

@ -400,6 +400,14 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
MapCursor<ValuePhiNode, PhiInfoElement> 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) {

View File

@ -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.type.TypeReference;
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.DuplicationReplacement;
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.BeginNode;
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.KillingBeginNode;
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.Equivalence;
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 @@ public class InliningUtil extends ValueMergeUtil {
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<ValueNode> parameters = invoke.callTarget().arguments();
assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
@ -291,7 +290,7 @@ public class InliningUtil extends ValueMergeUtil {
ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
ArrayList<InvokeNode> partialIntrinsicExits = new ArrayList<>();
ArrayList<Invoke> partialIntrinsicExits = new ArrayList<>();
UnwindNode unwindNode = null;
final StartNode entryPointNode = inlineGraph.start();
FixedNode firstCFGNode = entryPointNode.next();
@ -305,10 +304,10 @@ public class InliningUtil extends ValueMergeUtil {
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 @@ public class InliningUtil extends ValueMergeUtil {
assert invokeNode.successors().first() != null : invoke;
assert invokeNode.predecessor() != null;
UnmodifiableEconomicMap<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
EconomicMap<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
FrameState stateAfter = invoke.stateAfter();
assert stateAfter == null || stateAfter.isAlive();
@ -370,12 +369,16 @@ public class InliningUtil extends ValueMergeUtil {
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 @@ public class InliningUtil extends ValueMergeUtil {
}
/**
* 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 @@ public class InliningUtil extends ValueMergeUtil {
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 @@ public class InliningUtil extends ValueMergeUtil {
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<Node> seen = new NodeMap<>(graph);
ArrayDeque<Node> workList = new ArrayDeque<>();
ArrayDeque<ValueNode> 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 @@ public class InliningUtil extends ValueMergeUtil {
}
}
protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, UnmodifiableEconomicMap<Node, Node> duplicates, FrameState stateAtExceptionEdge,
protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, EconomicMap<Node, Node> duplicates, FrameState stateAtExceptionEdge,
boolean alwaysDuplicateStateAfter) {
FrameState stateAtReturn = invoke.stateAfter();
FrameState outerFrameState = null;
JavaKind invokeReturnKind = invoke.asNode().getStackKind();
EconomicMap<Node, Node> 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<Node, Node> replacements, ResolvedJavaMethod inlinedMethod, FrameState stateAtExceptionEdge,
FrameState outerFrameState,
boolean alwaysDuplicateStateAfter, ResolvedJavaMethod invokeTargetMethod, List<ValueNode> 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 @@ public class InliningUtil extends ValueMergeUtil {
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 @@ public class InliningUtil extends ValueMergeUtil {
}
}
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 @@ public class InliningUtil extends ValueMergeUtil {
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<Node, Node> replacements, boolean alwaysDuplicateStateAfter) {
Graph graph = nonReplaceableFrameState.graph();
NodeWorkList workList = graph.createNodeWorkList();
workList.add(nonReplaceableFrameState);
@ -686,6 +753,20 @@ public class InliningUtil extends ValueMergeUtil {
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) {

View File

@ -30,7 +30,6 @@ import org.graalvm.compiler.debug.DebugCloseable;
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 @@ public abstract class BasePhase<C> implements PhaseSizeContract {
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 @@ public abstract class BasePhase<C> implements PhaseSizeContract {
}
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

View File

@ -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();
}
}

View File

@ -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.ControlSinkNode;
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.memory.MemoryCheckpoint;
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 {

View File

@ -39,6 +39,7 @@ import org.graalvm.compiler.core.common.type.ObjectStamp;
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 @@ public class VerifyDebugUsage extends VerifyPhase<PhaseContext> {
protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, List<? extends ValueNode> 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);
}
}

View File

@ -38,6 +38,7 @@ import java.util.Map;
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 @@ public class BinaryGraphPrinter implements GraphPrinter {
return snippetReflection;
}
@SuppressWarnings("all")
@Override
public void print(Graph graph, String title, Map<Object, Object> properties) throws IOException {
public void print(Graph graph, Map<Object, Object> 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 @@ public class BinaryGraphPrinter implements GraphPrinter {
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 @@ public class BinaryGraphPrinter implements GraphPrinter {
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 @@ public class BinaryGraphPrinter implements GraphPrinter {
} 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());

View File

@ -79,7 +79,8 @@ public class CFGPrinterObserver implements DebugDumpHandler {
}
@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) {

View File

@ -258,7 +258,7 @@ public class CanonicalStringGraphPrinter implements GraphPrinter {
}
@Override
public void print(Graph graph, String title, Map<Object, Object> properties) throws IOException {
public void print(Graph graph, Map<Object, Object> 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 @@ public class CanonicalStringGraphPrinter implements GraphPrinter {
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)) {

View File

@ -80,7 +80,7 @@ public class GraalDebugConfigCustomizer implements DebugConfigCustomizer {
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);

View File

@ -53,7 +53,7 @@ interface GraphPrinter extends Closeable {
* Prints an entire {@link Graph} with the specified title, optionally using short names for
* nodes.
*/
void print(Graph graph, String title, Map<Object, Object> properties) throws IOException;
void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException;
SnippetReflectionProvider getSnippetReflectionProvider();

View File

@ -120,7 +120,7 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler {
@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 @@ public class GraphPrinterDumpHandler implements DebugDumpHandler {
}
}
addCFGFileName(properties);
printer.print(graph, nextDumpId() + ":" + message, properties);
printer.print(graph, properties, nextDumpId(), format, arguments);
} catch (IOException e) {
handleException(e);
} catch (Throwable e) {

View File

@ -114,7 +114,8 @@ public class IdealGraphPrinter extends BasicIdealGraphPrinter implements GraphPr
* nodes.
*/
@Override
public void print(Graph graph, String title, Map<Object, Object> properties) {
public void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) {
String title = id + ": " + String.format(format, args);
beginGraph(title);
EconomicSet<Node> noBlockNodes = EconomicSet.create(Equivalence.IDENTITY);
ScheduleResult schedule = null;

View File

@ -63,7 +63,7 @@ public class NoDeadCodeVerifyHandler implements DebugVerifyHandler {
private static final Map<String, Boolean> 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 @@ public class NoDeadCodeVerifyHandler implements DebugVerifyHandler {
List<Node> 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());

View File

@ -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 @@ import jdk.vm.ci.meta.ResolvedJavaMethod;
*/
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 @@ public class ReplacementsParseTest extends ReplacementsTest {
}
};
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 @@ public class ReplacementsParseTest extends ReplacementsTest {
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)
@ -145,6 +211,25 @@ public class ReplacementsParseTest extends ReplacementsTest {
@NodeIntrinsic(PiNode.class)
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.
*/
@ -166,6 +251,8 @@ public class ReplacementsParseTest extends ReplacementsTest {
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 @@ public class ReplacementsParseTest extends ReplacementsTest {
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 @@ public class ReplacementsParseTest extends ReplacementsTest {
}
}
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 class ReplacementsParseTest extends ReplacementsTest {
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;
}
}
}

View File

@ -29,6 +29,7 @@ import java.lang.reflect.Modifier;
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.FixedNode;
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;
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 @@ public class GraphKit implements GraphBuilderTool {
pushStructure(s);
}
private IfStructure saveLastNode() {
private IfStructure saveLastIfNode() {
IfStructure s = getTopStructure(IfStructure.class);
switch (s.state) {
case CONDITION:
@ -385,19 +389,19 @@ public class GraphKit implements GraphBuilderTool {
}
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 @@ public class GraphKit implements GraphBuilderTool {
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;
}
}

View File

@ -521,7 +521,9 @@ public abstract class PEGraphDecoder extends SimplifyingGraphDecoder {
* 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 @@ public abstract class PEGraphDecoder extends SimplifyingGraphDecoder {
*/
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);

View File

@ -52,6 +52,7 @@ import org.graalvm.compiler.nodes.type.StampTool;
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 class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt
public BasicArrayCopyNode(NodeClass<? extends AbstractMemoryCheckpoint> 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;
}

View File

@ -64,7 +64,7 @@ public abstract class BasicObjectCloneNode extends MacroStateSplitNode implement
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 @@ public abstract class BasicObjectCloneNode extends MacroStateSplitNode implement
*
* 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;
}

View File

@ -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.ArrayList;
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 @@ public final class SubprocessUtil {
* @param mainClassAndArgs the main class and its arguments
*/
public static Subprocess java(List<String> vmArgs, List<String> 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<String> vmArgs, Map<String, String> 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<String> vmArgs, Map<String, String> env, List<String> 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<String> vmArgs, Map<String, String> env, List<String> mainClassAndArgs) throws IOException, InterruptedException {
List<String> command = new ArrayList<>(vmArgs);
command.addAll(mainClassAndArgs);
ProcessBuilder processBuilder = new ProcessBuilder(command);
if (env != null) {
Map<String, String> processBuilderEnv = processBuilder.environment();
processBuilderEnv.putAll(env);
}
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));

View File

@ -26,7 +26,7 @@ import java.util.ArrayList;
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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
private boolean mergeObjectStates(int resultObject, int[] sourceObjects, PartialEscapeBlockState<?>[] states) {
boolean compatible = true;
boolean ensureVirtual = true;
IntFunction<Integer> 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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
// 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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
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 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
*
* @return true if materialization happened during the merge, false otherwise
*/
private boolean mergeObjectEntry(IntFunction<Integer> 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;