8181369: Update Graal
Reviewed-by: kvn
This commit is contained in:
parent
292ac89940
commit
993168dfd8
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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()) {
|
||||
|
@ -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();
|
||||
|
@ -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<>();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {i#inputName} or {p#propertyName}.
|
||||
* using {i#inputName} or {p#propertyName}. Returns empty string if no
|
||||
* special name template is specified.
|
||||
*/
|
||||
public String getNameTemplate() {
|
||||
return nameTemplate.isEmpty() ? shortName() : nameTemplate;
|
||||
return nameTemplate;
|
||||
}
|
||||
|
||||
interface InplaceUpdateClosure {
|
||||
@ -879,15 +879,9 @@ 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();
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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--) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user