# HG changeset patch # User Stefan Anzinger # Date 1429012878 -7200 # Node ID c617a74a9eaba5b724ba1f9cf277f703b789c209 # Parent d9713313e88c1c0fd9dcf1f5d6bcbee5e1caecf8# Parent cea0b72851900c95a2778102ce0be7644e6d29d4 Merge diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java --- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java Tue Apr 14 14:01:18 2015 +0200 @@ -71,15 +71,19 @@ Register[] getCallingConventionRegisters(Type type, Kind kind); /** - * Gets the set of registers that can be used by the register allocator. + * Gets the set of all registers that might be used by the register allocator. + * + * To get the set of registers the register allocator is allowed to use see + * {@link RegisterAllocationConfig#getAllocatableRegisters()} */ + @SuppressWarnings("javadoc") Register[] getAllocatableRegisters(); /** - * Gets the set of registers that can be used by the register allocator for a value of a - * particular kind. + * Filters a set of registers and returns only those that can be used by the register allocator + * for a value of a particular kind. */ - Register[] getAllocatableRegisters(PlatformKind kind); + Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers); /** * Gets the registers whose values must be preserved by a method across any call it makes. diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java --- a/graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.amd64.test/src/com/oracle/graal/compiler/amd64/test/AMD64AllocatorTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -22,6 +22,7 @@ */ package com.oracle.graal.compiler.amd64.test; +import static com.oracle.graal.compiler.common.GraalOptions.*; import static org.junit.Assume.*; import org.junit.*; @@ -34,6 +35,7 @@ @Before public void checkAMD64() { assumeTrue("skipping AMD64 specific test", getTarget().arch instanceof AMD64); + assumeTrue("RegisterPressure is set -> skip", RegisterPressure.getValue() == null); } @Test diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/alloc/RegisterAllocationConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/alloc/RegisterAllocationConfig.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.compiler.common.alloc; + +import static com.oracle.graal.compiler.common.GraalOptions.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; + +/** + * Configuration for register allocation. This is different to {@link RegisterConfig} as it only + * returns registers specified by {@link GraalOptions#RegisterPressure}. + */ +public class RegisterAllocationConfig { + + public static final String ALL_REGISTERS = ""; + + private static Register findRegister(String name, Register[] all) { + for (Register reg : all) { + if (reg.name.equals(name)) { + return reg; + } + } + throw new IllegalArgumentException("register " + name + " is not allocatable"); + } + + private static Register[] initAllocatable(Register[] registers) { + if (RegisterPressure.getValue() != null && !RegisterPressure.getValue().equals(ALL_REGISTERS)) { + String[] names = RegisterPressure.getValue().split(","); + Register[] regs = new Register[names.length]; + for (int i = 0; i < names.length; i++) { + regs[i] = findRegister(names[i], registers); + } + return regs; + } + + return registers; + } + + private final RegisterConfig registerConfig; + private final Map categorized = new HashMap<>(); + private Register[] cachedRegisters; + + public RegisterAllocationConfig(RegisterConfig registerConfig) { + this.registerConfig = registerConfig; + } + + /** + * Gets the set of registers that can be used by the register allocator for a value of a + * particular kind. + */ + public Register[] getAllocatableRegisters(PlatformKind kind) { + PlatformKind.Key key = kind.getKey(); + if (categorized.containsKey(key)) { + Register[] val = categorized.get(key); + return val; + } + + Register[] ret = registerConfig.filterAllocatableRegisters(kind, getAllocatableRegisters()); + categorized.put(key, ret); + return ret; + + } + + /** + * Gets the set of registers that can be used by the register allocator. + */ + public Register[] getAllocatableRegisters() { + if (cachedRegisters == null) { + cachedRegisters = initAllocatable(registerConfig.getAllocatableRegisters()); + } + assert cachedRegisters != null; + return cachedRegisters; + } + + public RegisterConfig getRegisterConfig() { + return registerConfig; + } + +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/AbstractObjectStamp.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/AbstractObjectStamp.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/AbstractObjectStamp.java Tue Apr 14 14:01:18 2015 +0200 @@ -98,9 +98,6 @@ if (this == otherStamp) { return this; } - if (!isCompatible(otherStamp)) { - return StampFactory.illegal(Kind.Illegal); - } AbstractObjectStamp other = (AbstractObjectStamp) otherStamp; if (isIllegal()) { return other; @@ -170,9 +167,6 @@ if (this == otherStamp) { return this; } - if (!isCompatible(otherStamp)) { - return StampFactory.illegal(Kind.Illegal); - } AbstractObjectStamp other = (AbstractObjectStamp) otherStamp; if (isIllegal()) { return this; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java Tue Apr 14 14:01:18 2015 +0200 @@ -176,9 +176,6 @@ if (otherStamp == this) { return this; } - if (!(otherStamp instanceof FloatStamp)) { - return StampFactory.illegal(Kind.Illegal); - } FloatStamp other = (FloatStamp) otherStamp; assert getBits() == other.getBits(); double meetUpperBound = meetBounds(upperBound, other.upperBound, Math::max); @@ -198,9 +195,6 @@ if (otherStamp == this) { return this; } - if (!(otherStamp instanceof FloatStamp)) { - return StampFactory.illegal(Kind.Illegal); - } FloatStamp other = (FloatStamp) otherStamp; assert getBits() == other.getBits(); double joinUpperBound = Math.min(upperBound, other.upperBound); diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IllegalStamp.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IllegalStamp.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IllegalStamp.java Tue Apr 14 14:01:18 2015 +0200 @@ -27,7 +27,9 @@ import com.oracle.graal.compiler.common.spi.*; /** - * This stamp represents the illegal type. Values with this type can not exist at run time. + * This stamp represents the type of the {@link Kind#Illegal} value in the second slot of + * {@link Kind#Long} and {@link Kind#Double} values. It can only appear in framestates or virtual + * objects. */ public final class IllegalStamp extends Stamp { @@ -56,7 +58,8 @@ @Override public Stamp constant(Constant c, MetaAccessProvider meta) { - throw GraalInternalError.shouldNotReachHere("illegal stamp has no value"); + assert ((PrimitiveConstant) c).getKind() == Kind.Illegal; + return this; } @Override @@ -66,17 +69,19 @@ @Override public Stamp meet(Stamp other) { + assert other instanceof IllegalStamp; return this; } @Override public Stamp join(Stamp other) { + assert other instanceof IllegalStamp; return this; } @Override public boolean isCompatible(Stamp stamp) { - return false; + return stamp instanceof IllegalStamp; } @Override @@ -91,6 +96,7 @@ @Override public Stamp improveWith(Stamp other) { + assert other instanceof IllegalStamp; return this; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java Tue Apr 14 14:01:18 2015 +0200 @@ -253,9 +253,6 @@ if (otherStamp == this) { return this; } - if (!(otherStamp instanceof IntegerStamp)) { - return StampFactory.illegal(Kind.Illegal); - } IntegerStamp other = (IntegerStamp) otherStamp; return createStamp(other, Math.max(upperBound, other.upperBound), Math.min(lowerBound, other.lowerBound), downMask & other.downMask, upMask | other.upMask); } @@ -265,9 +262,6 @@ if (otherStamp == this) { return this; } - if (!(otherStamp instanceof IntegerStamp)) { - return StampFactory.illegal(Kind.Illegal); - } IntegerStamp other = (IntegerStamp) otherStamp; long newDownMask = downMask | other.downMask; long newLowerBound = Math.max(lowerBound, other.lowerBound) | newDownMask; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/StampFactory.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/StampFactory.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/StampFactory.java Tue Apr 14 14:01:18 2015 +0200 @@ -76,10 +76,9 @@ for (Kind k : Kind.values()) { if (stampCache[k.ordinal()] != null) { illegalStampCache[k.ordinal()] = stampCache[k.ordinal()].illegal(); - } else { - illegalStampCache[k.ordinal()] = IllegalStamp.getInstance(); } } + illegalStampCache[Kind.Illegal.ordinal()] = IllegalStamp.getInstance(); } public static Stamp tautology() { @@ -122,10 +121,6 @@ return positiveInt; } - public static Stamp illegal() { - return illegal(Kind.Illegal); - } - public static Stamp illegal(Kind kind) { return illegalStampCache[kind.ordinal()]; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/VoidStamp.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/VoidStamp.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/VoidStamp.java Tue Apr 14 14:01:18 2015 +0200 @@ -46,10 +46,8 @@ @Override public Stamp improveWith(Stamp other) { - if (other instanceof VoidStamp) { - return this; - } - return StampFactory.illegal(Kind.Illegal); + assert other instanceof VoidStamp; + return this; } @Override @@ -74,29 +72,19 @@ @Override public Stamp meet(Stamp other) { - if (other instanceof IllegalStamp) { - return other.join(this); - } - if (this == other) { - return this; - } - return StampFactory.illegal(Kind.Illegal); + assert other instanceof VoidStamp; + return this; } @Override public Stamp join(Stamp other) { - if (other instanceof IllegalStamp) { - return other.join(this); - } - if (this == other) { - return this; - } - return StampFactory.illegal(Kind.Illegal); + assert other instanceof VoidStamp; + return this; } @Override public boolean isCompatible(Stamp stamp) { - return this == stamp; + return stamp instanceof VoidStamp; } @Override diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/UnsafeArrayTypeWriter.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/UnsafeArrayTypeWriter.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/util/UnsafeArrayTypeWriter.java Tue Apr 14 14:01:18 2015 +0200 @@ -37,12 +37,17 @@ */ public class UnsafeArrayTypeWriter implements TypeWriter { - private static final int CHUNK_SIZE = 4000; + private static final int MIN_CHUNK_LENGTH = 200; + private static final int MAX_CHUNK_LENGTH = 16000; static class Chunk { - protected final byte[] data = new byte[CHUNK_SIZE]; + protected final byte[] data; protected int size; protected Chunk next; + + protected Chunk(int arrayLength) { + data = new byte[arrayLength]; + } } private Chunk firstChunk; @@ -50,7 +55,7 @@ private int totalSize; public UnsafeArrayTypeWriter() { - firstChunk = new Chunk(); + firstChunk = new Chunk(MIN_CHUNK_LENGTH); writeChunk = firstChunk; } @@ -153,7 +158,7 @@ private long writeOffset(int writeBytes) { if (writeChunk.size + writeBytes >= writeChunk.data.length) { - Chunk newChunk = new Chunk(); + Chunk newChunk = new Chunk(Math.min(writeChunk.data.length * 2, MAX_CHUNK_LENGTH)); writeChunk.next = newChunk; writeChunk = newChunk; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java --- a/graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -22,6 +22,7 @@ */ package com.oracle.graal.compiler.sparc.test; +import static com.oracle.graal.compiler.common.GraalOptions.*; import static org.junit.Assume.*; import org.junit.*; @@ -34,6 +35,7 @@ @Before public void checkSPARC() { assumeTrue("skipping SPARC specific test", getTarget().arch instanceof SPARC); + assumeTrue("RegisterPressure is set -> skip", RegisterPressure.getValue() == null); } @Test diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -50,7 +50,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.java.*; import com.oracle.graal.lir.asm.*; import com.oracle.graal.lir.phases.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Tue Apr 14 14:01:18 2015 +0200 @@ -23,6 +23,8 @@ package com.oracle.graal.compiler; import static com.oracle.graal.compiler.GraalCompiler.Options.*; +import static com.oracle.graal.compiler.common.GraalOptions.*; +import static com.oracle.graal.compiler.common.alloc.RegisterAllocationConfig.*; import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*; import java.util.*; @@ -38,6 +40,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.lir.*; +import com.oracle.graal.lir.alloc.lsra.*; import com.oracle.graal.lir.asm.*; import com.oracle.graal.lir.framemap.*; import com.oracle.graal.lir.gen.*; @@ -49,6 +52,7 @@ import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.options.*; +import com.oracle.graal.options.OptionValue.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.schedule.*; @@ -241,6 +245,22 @@ public static LIRGenerationResult emitLIR(Backend backend, TargetDescription target, SchedulePhase schedule, StructuredGraph graph, Object stub, CallingConvention cc, RegisterConfig registerConfig, LIRSuites lirSuites) { + try { + return emitLIR0(backend, target, schedule, graph, stub, cc, registerConfig, lirSuites); + } catch (OutOfRegistersException e) { + if (RegisterPressure.getValue() != null && !RegisterPressure.getValue().equals(ALL_REGISTERS)) { + try (OverrideScope s = OptionValue.override(RegisterPressure, ALL_REGISTERS)) { + // retry with default register set + return emitLIR0(backend, target, schedule, graph, stub, cc, registerConfig, lirSuites); + } + } else { + throw e; + } + } + } + + private static LIRGenerationResult emitLIR0(Backend backend, TargetDescription target, SchedulePhase schedule, StructuredGraph graph, Object stub, CallingConvention cc, + RegisterConfig registerConfig, LIRSuites lirSuites) { try (Scope ds = Debug.scope("EmitLIR"); DebugCloseable a = EmitLIR.start()) { List blocks = schedule.getCFG().getBlocks(); Block startBlock = schedule.getCFG().getStartBlock(); diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Tue Apr 14 14:01:18 2015 +0200 @@ -304,7 +304,7 @@ } protected void emitNode(ValueNode node) { - if (Debug.isLogEnabled() && node.stamp() instanceof IllegalStamp) { + if (Debug.isLogEnabled() && node.stamp().isIllegal()) { Debug.log("This node has invalid type, we are emitting dead code(?): %s", node); } if (node instanceof LIRLowerable) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java Tue Apr 14 14:01:18 2015 +0200 @@ -433,7 +433,7 @@ this.movUsageFromEndTo(1); return true; } - for (int i = 0; i < this.extraUsagesCount; ++i) { + for (int i = this.extraUsagesCount - 1; i >= 0; i--) { if (extraUsages[i] == node) { this.movUsageFromEndTo(i + INLINE_USAGE_COUNT); return true; @@ -683,12 +683,12 @@ } private void unregisterInputs() { - for (Node input : inputs()) { - removeThisFromUsages(input); + acceptInputs((node, input) -> { + node.removeThisFromUsages(input); if (input.hasNoUsages()) { - maybeNotifyZeroUsages(input); + node.maybeNotifyZeroUsages(input); } - } + }); } public void clearInputs() { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/ForeignCallPlugin.java Tue Apr 14 14:01:18 2015 +0200 @@ -24,7 +24,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugin.java Tue Apr 14 14:01:18 2015 +0200 @@ -27,7 +27,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; /** diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPluginIdHolder.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPluginIdHolder.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +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 com.oracle.graal.graphbuilderconf; - -import com.oracle.graal.api.meta.*; - -/** - * {@link ResolvedJavaMethod}s that can assign themselves an identifier for use in a side table - * mapping methods to {@link InvocationPlugin}s. - */ -public interface InvocationPluginIdHolder extends ResolvedJavaMethod { - /** - * Sets the unique, positive, non-zero identifier for this method. - */ - void setInvocationPluginId(int id); - - /** - * Gets the identifier set by {@link #setInvocationPluginId(int)} or 0 if no - * {@link InvocationPlugin} identifier was assigned to this method. - */ - int getInvocationPluginId(); -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InvocationPlugins.java Tue Apr 14 14:01:18 2015 +0200 @@ -26,43 +26,19 @@ import java.lang.reflect.*; import java.util.*; -import java.util.stream.*; import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.graph.Node; +import com.oracle.graal.graph.*; import com.oracle.graal.graph.iterators.*; +import com.oracle.graal.graphbuilderconf.MethodIdMap.MethodKey; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.type.*; /** * Manages a set of {@link InvocationPlugin}s. */ public class InvocationPlugins { - /** - * Access to the receiver in an {@link InvocationPlugin} for a non-static method. The class - * literal for this interface must be used with - * {@link InvocationPlugins#register(InvocationPlugin, Class, String, Class...)} to denote the - * receiver argument for such a non-static method. - */ - public interface Receiver { - /** - * Gets the receiver value, null checking it first if necessary. - * - * @return the receiver value with a {@linkplain StampTool#isPointerNonNull(ValueNode) - * non-null} stamp - */ - ValueNode get(); - - /** - * Determines if the receiver is constant. - */ - default boolean isConstant() { - return false; - } - } - public static class InvocationPluginReceiver implements Receiver { private final GraphBuilderContext parser; private ValueNode[] args; @@ -195,101 +171,16 @@ } } - static final class MethodInfo { - final boolean isStatic; - final Class declaringClass; - final String name; - final Class[] argumentTypes; - final InvocationPlugin plugin; - - int id; - - MethodInfo(InvocationPlugin plugin, Class declaringClass, String name, Class... argumentTypes) { - this.plugin = plugin; - this.isStatic = argumentTypes.length == 0 || argumentTypes[0] != Receiver.class; - this.declaringClass = declaringClass; - this.name = name; - this.argumentTypes = argumentTypes; - if (!isStatic) { - argumentTypes[0] = declaringClass; - } - assert resolveJava() != null; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof MethodInfo) { - MethodInfo that = (MethodInfo) obj; - boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes); - assert !res || this.isStatic == that.isStatic; - return res; - } - return false; - } - - @Override - public int hashCode() { - // Replay compilation mandates use of stable hash codes - return declaringClass.getName().hashCode() ^ name.hashCode(); - } - - ResolvedJavaMethod resolve(MetaAccessProvider metaAccess) { - return metaAccess.lookupJavaMethod(resolveJava()); - } - - Executable resolveJava() { - try { - Executable res; - Class[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length); - if (name.equals("")) { - res = declaringClass.getDeclaredConstructor(parameterTypes); - } else { - res = declaringClass.getDeclaredMethod(name, parameterTypes); - } - assert Modifier.isStatic(res.getModifiers()) == isStatic; - return res; - } catch (NoSuchMethodException | SecurityException e) { - throw new GraalInternalError(e); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('('); - for (Class p : argumentTypes) { - if (sb.charAt(sb.length() - 1) != '(') { - sb.append(", "); - } - sb.append(p.getSimpleName()); - } - return sb.append(')').toString(); - } - } - - protected final MetaAccessProvider metaAccess; - private final List registrations; - - /** - * The minimum {@linkplain InvocationPluginIdHolder#getInvocationPluginId() id} for a method - * associated with a plugin in {@link #plugins}. - */ - private int minId = Integer.MAX_VALUE; - - /** - * Resolved methods to plugins map. The keys (i.e., indexes) are derived from - * {@link InvocationPluginIdHolder#getInvocationPluginId()}. - */ - private volatile InvocationPlugin[] plugins; + protected final MethodIdMap plugins; /** * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in * this object. */ - private InvocationPlugins parent; + protected final InvocationPlugins parent; private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { - this.metaAccess = metaAccess; - this.registrations = new ArrayList<>(INITIAL_PLUGIN_CAPACITY); + this.plugins = new MethodIdMap<>(metaAccess); InvocationPlugins p = parent; // Only adopt a non-empty parent while (p != null && p.size() == 0) { @@ -298,13 +189,11 @@ this.parent = p; } - private static final int INITIAL_PLUGIN_CAPACITY = 64; - /** * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. */ public InvocationPlugins(InvocationPlugins parent) { - this(parent, parent.metaAccess); + this(parent, parent.plugins.getMetaAccess()); } public InvocationPlugins(MetaAccessProvider metaAccess) { @@ -316,14 +205,10 @@ * registered for {@code method}. */ public void register(InvocationPlugin plugin, Class declaringClass, String name, Class... argumentTypes) { - MethodInfo methodInfo = new MethodInfo(plugin, declaringClass, name, argumentTypes); + MethodKey methodInfo = plugins.put(plugin, declaringClass, name, argumentTypes); assert Checker.check(this, methodInfo, plugin); - assert plugins == null : "invocation plugin registration is closed"; - registrations.add(methodInfo); } - private static int nextInvocationPluginId = 1; - /** * Gets the plugin for a given method. * @@ -331,54 +216,14 @@ * @return the plugin associated with {@code method} or {@code null} if none exists */ public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { - assert method instanceof InvocationPluginIdHolder; + assert method instanceof MethodIdHolder; if (parent != null) { InvocationPlugin plugin = parent.lookupInvocation(method); if (plugin != null) { return plugin; } } - InvocationPluginIdHolder pluggable = (InvocationPluginIdHolder) method; - if (plugins == null) { - // Must synchronize across all InvocationPlugins objects to ensure thread safe - // allocation of InvocationPlugin identifiers - synchronized (InvocationPlugins.class) { - if (plugins == null) { - if (registrations.isEmpty()) { - plugins = new InvocationPlugin[0]; - } else { - int max = Integer.MIN_VALUE; - for (MethodInfo methodInfo : registrations) { - InvocationPluginIdHolder p = (InvocationPluginIdHolder) methodInfo.resolve(metaAccess); - int id = p.getInvocationPluginId(); - if (id == 0) { - id = nextInvocationPluginId++; - p.setInvocationPluginId(id); - } - if (id < minId) { - minId = id; - } - if (id > max) { - max = id; - - } - methodInfo.id = id; - } - - int length = (max - minId) + 1; - plugins = new InvocationPlugin[length]; - for (MethodInfo m : registrations) { - int index = m.id - minId; - plugins[index] = m.plugin; - } - } - } - } - } - - int id = pluggable.getInvocationPluginId(); - int index = id - minId; - return index >= 0 && index < plugins.length ? plugins[index] : null; + return plugins.get((MethodIdHolder) method); } /** @@ -391,7 +236,7 @@ @Override public String toString() { - return registrations.stream().map(MethodInfo::toString).collect(Collectors.joining(", ")) + " / parent: " + this.parent; + return plugins + " / parent: " + this.parent; } private static class Checker { @@ -420,10 +265,10 @@ SIGS = sigs.toArray(new Class[sigs.size()][]); } - public static boolean check(InvocationPlugins plugins, MethodInfo method, InvocationPlugin plugin) { - InvocationPlugins p = plugins; + public static boolean check(InvocationPlugins plugins, MethodKey method, InvocationPlugin plugin) { + InvocationPlugins p = plugins.parent; while (p != null) { - assert !p.registrations.contains(method) : "a plugin is already registered for " + method; + assert !p.plugins.containsKey(method) : "a plugin is already registered for " + method; p = p.parent; } if (plugin instanceof ForeignCallPlugin) { @@ -448,12 +293,8 @@ } } - public MetaAccessProvider getMetaAccess() { - return metaAccess; - } - public int size() { - return registrations.size(); + return plugins.size(); } /** diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodIdHolder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodIdHolder.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,96 @@ +/* + * 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 com.oracle.graal.graphbuilderconf; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.api.meta.*; + +/** + * {@link ResolvedJavaMethod}s that can be assigned a globally unique identifier for use as keys in + * a {@link MethodIdMap}. This should only be used where the cost of a {@link Map} with + * {@link ResolvedJavaMethod}s as keys is too high. + */ +public interface MethodIdHolder extends ResolvedJavaMethod { + /** + * Sets the unique, positive, non-zero identifier for this method. + */ + void setMethodId(int id); + + /** + * Gets the identifier set by {@link #setMethodId(int)} or 0 if no identifier was assigned to + * this method. + */ + int getMethodId(); + + /** + * A singleton class for allocating globally unique method identifiers. + */ + static final class MethodIdAllocator { + + /** + * Ensures a given method has a unique identifier. + */ + public int assignId(MethodIdHolder holder) { + assert Thread.holdsLock(instance) : "must only be called from within MethodIdHolder.allocateIds"; + int id = holder.getMethodId(); + if (id == 0) { + id = nextId++; + holder.setMethodId(id); + if (idVerifierMap != null) { + idVerifierMap.put(holder, id); + } + } else { + assert idVerifierMap.get(holder) == id; + } + return id; + } + + private int nextId = 1; + private final Map idVerifierMap; + + @SuppressWarnings("all") + private MethodIdAllocator() { + boolean assertionsEnabled = false; + assert assertionsEnabled = true; + idVerifierMap = assertionsEnabled ? new HashMap<>() : null; + } + + /** + * Singleton instance. + */ + private static final MethodIdAllocator instance = new MethodIdAllocator(); + } + + /** + * Executes some given code that ensures some set of {@link ResolvedJavaMethod}s have unique ids + * {@linkplain MethodIdHolder#setMethodId(int) assigned} to them. The + * {@link Consumer#accept(Object)} method of the given object is called under a global lock. + */ + static void assignIds(Consumer methodIdConsumer) { + synchronized (MethodIdAllocator.instance) { + methodIdConsumer.accept(MethodIdAllocator.instance); + } + } +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodIdMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodIdMap.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,253 @@ +/* + * 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 com.oracle.graal.graphbuilderconf; + +import java.lang.reflect.*; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.graphbuilderconf.MethodIdHolder.MethodIdAllocator; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.type.*; + +/** + * A map whose keys are {@link MethodIdHolder}s that doesn't require eager resolution of + * {@link ResolvedJavaMethod}s and has retrieval as fast as array indexing. The constraints on using + * such a map are: + *
    + *
  • at most one value can be added for any key
  • + *
  • no more entries can be added after the first {@linkplain #get(MethodIdHolder) retrieval}
  • + *
+ * + * @param the type of the values in the map + */ +public class MethodIdMap { + + /** + * The receiver in a non-static method. The class literal for this interface must be used with + * {@link MethodIdMap#put(Object, Class, String, Class...)} to denote the receiver argument for + * such a non-static method. + */ + public interface Receiver { + /** + * Gets the receiver value, null checking it first if necessary. + * + * @return the receiver value with a {@linkplain StampTool#isPointerNonNull(ValueNode) + * non-null} stamp + */ + ValueNode get(); + + /** + * Determines if the receiver is constant. + */ + default boolean isConstant() { + return false; + } + } + + /** + * Key for a method. + */ + public static class MethodKey { + final boolean isStatic; + final Class declaringClass; + final String name; + final Class[] argumentTypes; + final T value; + int id; + + MethodKey(T data, Class declaringClass, String name, Class... argumentTypes) { + this.value = data; + this.isStatic = argumentTypes.length == 0 || argumentTypes[0] != Receiver.class; + this.declaringClass = declaringClass; + this.name = name; + this.argumentTypes = argumentTypes; + if (!isStatic) { + argumentTypes[0] = declaringClass; + } + assert resolveJava() != null; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MethodKey) { + MethodKey that = (MethodKey) obj; + boolean res = this.name.equals(that.name) && this.declaringClass.equals(that.declaringClass) && Arrays.equals(this.argumentTypes, that.argumentTypes); + assert !res || this.isStatic == that.isStatic; + return res; + } + return false; + } + + @Override + public int hashCode() { + // Replay compilation mandates use of stable hash codes + return declaringClass.getName().hashCode() ^ name.hashCode(); + } + + private MethodIdHolder resolve(MetaAccessProvider metaAccess) { + return (MethodIdHolder) metaAccess.lookupJavaMethod(resolveJava()); + } + + private Executable resolveJava() { + try { + Executable res; + Class[] parameterTypes = isStatic ? argumentTypes : Arrays.copyOfRange(argumentTypes, 1, argumentTypes.length); + if (name.equals("")) { + res = declaringClass.getDeclaredConstructor(parameterTypes); + } else { + res = declaringClass.getDeclaredMethod(name, parameterTypes); + } + assert Modifier.isStatic(res.getModifiers()) == isStatic; + return res; + } catch (NoSuchMethodException | SecurityException e) { + throw new GraalInternalError(e); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(declaringClass.getName()).append('.').append(name).append('('); + for (Class p : argumentTypes) { + if (sb.charAt(sb.length() - 1) != '(') { + sb.append(", "); + } + sb.append(p.getSimpleName()); + } + return sb.append(')').toString(); + } + } + + private final MetaAccessProvider metaAccess; + + /** + * Initial list of entries. + */ + private final List> registrations; + + /** + * Entry array that is initialized upon first call to {@link #get(MethodIdHolder)}. + * + * Note: this must be volatile since double-checked locking is used to initialize it + */ + private volatile V[] entries; + + /** + * The minimum {@linkplain MethodIdHolder#getMethodId() id} for a key in this map. + */ + private int minId = Integer.MAX_VALUE; + + public MethodIdMap(MetaAccessProvider metaAccess) { + this.metaAccess = metaAccess; + this.registrations = new ArrayList<>(INITIAL_CAPACITY); + } + + private static final int INITIAL_CAPACITY = 64; + + /** + * Adds an entry to this map for a specified method. + * + * @param value value to be associated with the specified method + * @param declaringClass the class declaring the method + * @param name the name of the method + * @param argumentTypes the argument types of the method. Element 0 of this array must be the + * {@link Class} value for {@link Receiver} iff the method is non-static. + * @return an object representing the method + */ + public MethodKey put(V value, Class declaringClass, String name, Class... argumentTypes) { + MethodKey methodKey = new MethodKey<>(value, declaringClass, name, argumentTypes); + assert entries == null : "registration is closed"; + assert !registrations.contains(methodKey) : "a value is already registered for " + methodKey; + registrations.add(methodKey); + return methodKey; + } + + @SuppressWarnings("unchecked") + protected V[] allocateEntries(int length) { + return (V[]) new Object[length]; + } + + /** + * Determines if a method denoted by a given {@link MethodKey} is in this map. + */ + public boolean containsKey(MethodKey key) { + return registrations.contains(key); + } + + public V get(MethodIdHolder method) { + if (entries == null) { + // 'assignIds' synchronizes on a global lock which ensures thread safe + // allocation of identifiers across all MethodIdHolder objects + MethodIdHolder.assignIds(new Consumer() { + + public void accept(MethodIdAllocator idAllocator) { + if (entries == null) { + if (registrations.isEmpty()) { + entries = allocateEntries(0); + } else { + int max = Integer.MIN_VALUE; + for (MethodKey methodKey : registrations) { + MethodIdHolder m = methodKey.resolve(metaAccess); + int id = idAllocator.assignId(m); + if (id < minId) { + minId = id; + } + if (id > max) { + max = id; + } + methodKey.id = id; + } + + int length = (max - minId) + 1; + entries = allocateEntries(length); + for (MethodKey m : registrations) { + int index = m.id - minId; + entries[index] = m.value; + } + } + } + } + }); + } + + int id = method.getMethodId(); + int index = id - minId; + return index >= 0 && index < entries.length ? entries[index] : null; + } + + @Override + public String toString() { + return registrations.stream().map(MethodKey::toString).collect(Collectors.joining(", ")); + } + + public MetaAccessProvider getMetaAccess() { + return metaAccess; + } + + public int size() { + return registrations.size(); + } +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java --- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/MethodSubstitutionPlugin.java Tue Apr 14 14:01:18 2015 +0200 @@ -30,7 +30,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; /** @@ -121,8 +121,8 @@ return m; } } - throw new GraalInternalError("No method found in %s compatible with the signature (%s)", declaringClass.getName(), Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect( - Collectors.joining(","))); + throw new GraalInternalError("No method found in %s compatible with \"%s(%s)\"", declaringClass.getName(), name, Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect( + Collectors.joining(", "))); } /** diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Tue Apr 14 14:01:18 2015 +0200 @@ -192,7 +192,7 @@ private Register findPollOnReturnScratchRegister() { RegisterConfig regConfig = getProviders().getCodeCache().getRegisterConfig(); - for (Register r : regConfig.getAllocatableRegisters(Kind.Long)) { + for (Register r : regConfig.getAllocatableRegisters()) { if (!r.equals(regConfig.getReturnRegister(Kind.Long)) && !r.equals(AMD64.rbp)) { return r; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Tue Apr 14 14:01:18 2015 +0200 @@ -26,7 +26,6 @@ import static com.oracle.graal.compiler.common.GraalOptions.*; import java.util.*; -import java.util.concurrent.*; import com.oracle.graal.amd64.*; import com.oracle.graal.api.code.*; @@ -52,8 +51,6 @@ private final boolean allAllocatableAreCallerSaved; - private final Map categorized = new ConcurrentHashMap<>(); - private final RegisterAttributes[] attributesMap; public int getMaximumFrameSize() { @@ -65,22 +62,15 @@ return allocatable.clone(); } - public Register[] getAllocatableRegisters(PlatformKind kind) { - PlatformKind.Key key = kind.getKey(); - if (categorized.containsKey(key)) { - Register[] val = categorized.get(key); - return val; - } - + public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) { ArrayList list = new ArrayList<>(); - for (Register reg : getAllocatableRegisters()) { + for (Register reg : registers) { if (architecture.canStoreValue(reg.getRegisterCategory(), kind)) { list.add(reg); } } Register[] ret = list.toArray(new Register[list.size()]); - categorized.put(key, ret); return ret; } @@ -101,15 +91,6 @@ private final CalleeSaveLayout csl; - private static Register findRegister(String name, Register[] all) { - for (Register reg : all) { - if (reg.name.equals(name)) { - return reg; - } - } - throw new IllegalArgumentException("register " + name + " is not allocatable"); - } - private static Register[] initAllocatable(boolean reserveForHeapBase) { Register[] registers = null; // @formatter:off @@ -127,16 +108,6 @@ }; } // @formatter:on - - if (RegisterPressure.getValue() != null) { - String[] names = RegisterPressure.getValue().split(","); - Register[] regs = new Register[names.length]; - for (int i = 0; i < names.length; i++) { - regs[i] = findRegister(names[i], registers); - } - return regs; - } - return registers; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java --- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java Tue Apr 14 14:01:18 2015 +0200 @@ -22,11 +22,9 @@ */ package com.oracle.graal.hotspot.sparc; -import static com.oracle.graal.compiler.common.GraalOptions.*; import static com.oracle.graal.sparc.SPARC.*; import java.util.*; -import java.util.concurrent.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.code.CallingConvention.Type; @@ -43,8 +41,6 @@ private final Register[] allocatable; - private final Map categorized = new ConcurrentHashMap<>(20); - private final RegisterAttributes[] attributesMap; @Override @@ -52,13 +48,9 @@ return allocatable.clone(); } - public Register[] getAllocatableRegisters(PlatformKind kind) { - if (categorized.containsKey(kind)) { - return categorized.get(kind); - } - + public Register[] filterAllocatableRegisters(PlatformKind kind, Register[] registers) { ArrayList list = new ArrayList<>(); - for (Register reg : getAllocatableRegisters()) { + for (Register reg : registers) { if (architecture.canStoreValue(reg.getRegisterCategory(), kind)) { // Special treatment for double precision // TODO: This is wasteful it uses only half of the registers as float. @@ -77,7 +69,6 @@ } Register[] ret = list.toArray(new Register[list.size()]); - categorized.put(kind, ret); return ret; } @@ -111,15 +102,6 @@ private final CalleeSaveLayout csl; - private static Register findRegister(String name, Register[] all) { - for (Register reg : all) { - if (reg.name.equals(name)) { - return reg; - } - } - throw new IllegalArgumentException("register " + name + " is not allocatable"); - } - private static Register[] initAllocatable(boolean reserveForHeapBase) { Register[] registers = null; if (reserveForHeapBase) { @@ -158,15 +140,6 @@ // @formatter:on } - if (RegisterPressure.getValue() != null) { - String[] names = RegisterPressure.getValue().split(","); - Register[] regs = new Register[names.length]; - for (int i = 0; i < names.length; i++) { - regs[i] = findRegister(names[i], registers); - } - return regs; - } - return registers; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -22,7 +22,7 @@ */ package com.oracle.graal.hotspot.test; -import static com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext.*; +import static com.oracle.graal.java.IntrinsicContext.*; import java.io.*; import java.lang.reflect.*; @@ -39,7 +39,6 @@ import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult; import com.oracle.graal.hotspot.meta.*; -import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java Tue Apr 14 14:01:18 2015 +0200 @@ -27,7 +27,6 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.api.replacements.*; -import com.oracle.graal.compiler.common.*; import com.oracle.graal.hotspot.replacements.*; import com.oracle.graal.hotspot.word.*; import com.oracle.graal.phases.util.*; @@ -68,14 +67,6 @@ if (!config.useCRC32Intrinsics) { return null; } - } else if (substituteClass == StringSubstitutions.class) { - /* - * AMD64's String.equals substitution needs about 8 registers so we better disable the - * substitution if there is some register pressure. - */ - if (GraalOptions.RegisterPressure.getValue() != null) { - return null; - } } return super.registerMethodSubstitution(cr, originalMethod, substituteMethod); } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Tue Apr 14 14:01:18 2015 +0200 @@ -34,8 +34,8 @@ import com.oracle.graal.api.replacements.*; import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.hotspot.nodes.ClassQueryNode.Query; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java Tue Apr 14 14:01:18 2015 +0200 @@ -33,6 +33,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.iterators.*; import com.oracle.graal.graphbuilderconf.*; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.phases.*; import com.oracle.graal.hotspot.replacements.*; @@ -77,7 +78,7 @@ @Override public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { if (methodHandleClass == null) { - methodHandleClass = metaAccess.lookupJavaType(MethodHandle.class); + methodHandleClass = plugins.getMetaAccess().lookupJavaType(MethodHandle.class); } if (method.getDeclaringClass().equals(methodHandleClass)) { HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethodImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethodImpl.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethodImpl.java Tue Apr 14 14:01:18 2015 +0200 @@ -42,7 +42,7 @@ /** * Implementation of {@link JavaMethod} for resolved HotSpot methods. */ -public final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, HotSpotProxified, InvocationPluginIdHolder { +public final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, HotSpotProxified, MethodIdHolder { private static final long serialVersionUID = -5486975070147586588L; @@ -742,14 +742,14 @@ return runtime().getCompilerToVM().hasCompiledCodeForOSR(metaspaceMethod, entryBCI, level); } - private int invocationPluginId; + private int methodId; - public void setInvocationPluginId(int id) { - assert invocationPluginId == 0; - invocationPluginId = id; + public void setMethodId(int id) { + assert methodId == 0; + methodId = id; } - public int getInvocationPluginId() { - return invocationPluginId; + public int getMethodId() { + return methodId; } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/type/MetaspacePointerStamp.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/type/MetaspacePointerStamp.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/type/MetaspacePointerStamp.java Tue Apr 14 14:01:18 2015 +0200 @@ -40,9 +40,7 @@ @Override public Stamp meet(Stamp other) { - if (!isCompatible(other)) { - return StampFactory.illegal(); - } + assert isCompatible(other); return this; } @@ -53,9 +51,7 @@ @Override public Stamp join(Stamp other) { - if (!isCompatible(other)) { - return StampFactory.illegal(); - } + assert isCompatible(other); return this; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java Tue Apr 14 14:01:18 2015 +0200 @@ -32,7 +32,6 @@ import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.meta.*; -import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java Tue Apr 14 14:01:18 2015 +0200 @@ -20,30 +20,24 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package com.oracle.graal.java; import static com.oracle.graal.api.code.TypeCheckHints.*; import static com.oracle.graal.api.meta.DeoptimizationReason.*; import static com.oracle.graal.bytecode.Bytecodes.*; import static com.oracle.graal.java.AbstractBytecodeParser.Options.*; -import static com.oracle.graal.java.HIRFrameStateBuilder.*; import java.util.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.replacements.*; import com.oracle.graal.bytecode.*; import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.calc.*; import com.oracle.graal.debug.*; import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement; import com.oracle.graal.java.BciBlockMapping.BciBlock; -import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.type.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.*; @@ -83,153 +77,6 @@ } /** - * Information about a substitute method being parsed in lieu of an original method. This can - * happen when a call to a {@link MethodSubstitution} is encountered or the root of compilation - * is a {@link MethodSubstitution} or a snippet. - */ - public static class ReplacementContext implements Replacement { - /** - * The method being replaced. - */ - final ResolvedJavaMethod method; - - /** - * The replacement method. - */ - final ResolvedJavaMethod replacement; - - public ReplacementContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute) { - this.method = method; - this.replacement = substitute; - } - - public ResolvedJavaMethod getOriginalMethod() { - return method; - } - - public ResolvedJavaMethod getReplacementMethod() { - return replacement; - } - - public boolean isIntrinsic() { - return false; - } - - /** - * Determines if a call within the compilation scope of a replacement represents a call to - * the original method. - */ - public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { - return method.equals(targetMethod) || replacement.equals(targetMethod); - } - - IntrinsicContext asIntrinsic() { - return null; - } - - @Override - public String toString() { - return "Replacement{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + "}"; - } - } - - /** - * Context for a replacement being inlined as a compiler intrinsic. Deoptimization within a - * compiler intrinsic must replay the intrinsified call. This context object retains the - * information required to build a frame state denoting the JVM state just before the - * intrinsified call. - */ - public static class IntrinsicContext extends ReplacementContext { - - /** - * BCI denoting an intrinsic is being parsed for inlining after the caller has been parsed. - */ - public static final int POST_PARSE_INLINE_BCI = -1; - - /** - * BCI denoting an intrinsic is the compilation root. - */ - public static final int ROOT_COMPILATION_BCI = -2; - - /** - * The arguments to the intrinsic. - */ - ValueNode[] args; - - /** - * The BCI of the intrinsified invocation, {@link #POST_PARSE_INLINE_BCI} or - * {@link #ROOT_COMPILATION_BCI}. - */ - final int bci; - - private FrameState stateBeforeCache; - - public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute, ValueNode[] args, int bci) { - super(method, substitute); - assert bci != POST_PARSE_INLINE_BCI || args == null; - this.args = args; - this.bci = bci; - assert !isCompilationRoot() || method.hasBytecodes() : "Cannot intrinsic for native or abstract method " + method.format("%H.%n(%p)"); - } - - @Override - public boolean isIntrinsic() { - return true; - } - - public boolean isPostParseInlined() { - return bci == POST_PARSE_INLINE_BCI; - } - - public boolean isCompilationRoot() { - return bci == ROOT_COMPILATION_BCI; - } - - public FrameState getInvokeStateBefore(StructuredGraph graph, BytecodeParser parent) { - if (isCompilationRoot()) { - int maxLocals = method.getMaxLocals(); - // The 'args' were initialized based on the intrinsic method but a - // frame state's 'locals' needs to have the same length as the frame - // state method's 'max_locals'. - ValueNode[] locals = maxLocals == args.length ? args : Arrays.copyOf(args, maxLocals); - ValueNode[] stack = EMPTY_ARRAY; - int stackSize = 0; - ValueNode[] locks = EMPTY_ARRAY; - List monitorIds = Collections.emptyList(); - return graph.add(new FrameState(null, method, 0, locals, stack, stackSize, locks, monitorIds, false, false)); - } else if (isPostParseInlined()) { - return graph.add(new FrameState(BytecodeFrame.BEFORE_BCI)); - } else { - assert !parent.parsingReplacement() || parent.replacementContext instanceof IntrinsicContext; - if (stateBeforeCache == null) { - assert stateBeforeCache == null; - - // Find the non-intrinsic ancestor calling the intrinsified method - BytecodeParser ancestor = parent; - while (ancestor.parsingReplacement()) { - assert ancestor.replacementContext instanceof IntrinsicContext; - ancestor = ancestor.getParent(); - } - FrameState stateDuring = ancestor.getFrameState().create(ancestor.bci(), ancestor.getParent(), true); - stateBeforeCache = stateDuring.duplicateModifiedBeforeCall(bci, Kind.Void, args); - } - return stateBeforeCache; - } - } - - @Override - IntrinsicContext asIntrinsic() { - return this; - } - - @Override - public String toString() { - return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + ", bci: " + bci + - (args == null ? "" : ", args: " + Arrays.toString(args)) + (stateBeforeCache == null ? "" : ", stateBefore: " + stateBeforeCache) + "}"; - } - } - - /** * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the * bytecode instructions as they are parsed. */ diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Apr 14 14:01:18 2015 +0200 @@ -50,7 +50,6 @@ import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo; import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; -import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext; import com.oracle.graal.java.BciBlockMapping.BciBlock; import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock; import com.oracle.graal.nodeinfo.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java Tue Apr 14 14:01:18 2015 +0200 @@ -32,7 +32,6 @@ import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.debug.*; import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext; import com.oracle.graal.java.BciBlockMapping.BciBlock; import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser; import com.oracle.graal.nodeinfo.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,128 @@ +/* + * 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 com.oracle.graal.java; + +import static com.oracle.graal.java.HIRFrameStateBuilder.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.java.GraphBuilderPhase.Instance.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; + +/** + * Context for a replacement being inlined as a compiler intrinsic. Deoptimization within a compiler + * intrinsic must replay the intrinsified call. This context object retains the information required + * to build a frame state denoting the JVM state just before the intrinsified call. + */ +public class IntrinsicContext extends ReplacementContext { + + /** + * BCI denoting an intrinsic is being parsed for inlining after the caller has been parsed. + */ + public static final int POST_PARSE_INLINE_BCI = -1; + + /** + * BCI denoting an intrinsic is the compilation root. + */ + public static final int ROOT_COMPILATION_BCI = -2; + + /** + * The arguments to the intrinsic. + */ + ValueNode[] args; + + /** + * The BCI of the intrinsified invocation, {@link #POST_PARSE_INLINE_BCI} or + * {@link #ROOT_COMPILATION_BCI}. + */ + final int bci; + + private FrameState stateBeforeCache; + + public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute, ValueNode[] args, int bci) { + super(method, substitute); + assert bci != POST_PARSE_INLINE_BCI || args == null; + this.args = args; + this.bci = bci; + assert !isCompilationRoot() || method.hasBytecodes() : "Cannot intrinsic for native or abstract method " + method.format("%H.%n(%p)"); + } + + @Override + public boolean isIntrinsic() { + return true; + } + + public boolean isPostParseInlined() { + return bci == POST_PARSE_INLINE_BCI; + } + + public boolean isCompilationRoot() { + return bci == ROOT_COMPILATION_BCI; + } + + public FrameState getInvokeStateBefore(StructuredGraph graph, BytecodeParser parent) { + if (isCompilationRoot()) { + int maxLocals = method.getMaxLocals(); + // The 'args' were initialized based on the intrinsic method but a + // frame state's 'locals' needs to have the same length as the frame + // state method's 'max_locals'. + ValueNode[] locals = maxLocals == args.length ? args : Arrays.copyOf(args, maxLocals); + ValueNode[] stack = EMPTY_ARRAY; + int stackSize = 0; + ValueNode[] locks = EMPTY_ARRAY; + List monitorIds = Collections.emptyList(); + return graph.add(new FrameState(null, method, 0, locals, stack, stackSize, locks, monitorIds, false, false)); + } else if (isPostParseInlined()) { + return graph.add(new FrameState(BytecodeFrame.BEFORE_BCI)); + } else { + assert !parent.parsingReplacement() || parent.replacementContext instanceof IntrinsicContext; + if (stateBeforeCache == null) { + assert stateBeforeCache == null; + + // Find the non-intrinsic ancestor calling the intrinsified method + BytecodeParser ancestor = parent; + while (ancestor.parsingReplacement()) { + assert ancestor.replacementContext instanceof IntrinsicContext; + ancestor = ancestor.getParent(); + } + FrameState stateDuring = ancestor.getFrameState().create(ancestor.bci(), ancestor.getParent(), true); + stateBeforeCache = stateDuring.duplicateModifiedBeforeCall(bci, Kind.Void, args); + } + return stateBeforeCache; + } + } + + @Override + IntrinsicContext asIntrinsic() { + return this; + } + + @Override + public String toString() { + return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + ", bci: " + bci + (args == null ? "" : ", args: " + Arrays.toString(args)) + + (stateBeforeCache == null ? "" : ", stateBefore: " + stateBeforeCache) + "}"; + } +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,78 @@ +/* + * 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 com.oracle.graal.java; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement; + +/** + * Information about a substitute method being parsed in lieu of an original method. This can happen + * when a call to a {@link MethodSubstitution} is encountered or the root of compilation is a + * {@link MethodSubstitution} or a snippet. + */ +public class ReplacementContext implements Replacement { + /** + * The method being replaced. + */ + final ResolvedJavaMethod method; + + /** + * The replacement method. + */ + final ResolvedJavaMethod replacement; + + public ReplacementContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute) { + this.method = method; + this.replacement = substitute; + } + + public ResolvedJavaMethod getOriginalMethod() { + return method; + } + + public ResolvedJavaMethod getReplacementMethod() { + return replacement; + } + + public boolean isIntrinsic() { + return false; + } + + /** + * Determines if a call within the compilation scope of a replacement represents a call to the + * original method. + */ + public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) { + return method.equals(targetMethod) || replacement.equals(targetMethod); + } + + IntrinsicContext asIntrinsic() { + return null; + } + + @Override + public String toString() { + return "Replacement{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + "}"; + } +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java Tue Apr 14 14:01:18 2015 +0200 @@ -66,6 +66,7 @@ final SpillMoveFactory spillMoveFactory; final RegisterAttributes[] registerAttributes; final Register[] registers; + final RegisterAllocationConfig regAllocConfig; final boolean callKillsRegisters; @@ -163,14 +164,15 @@ */ private final int firstVariableNumber; - LinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory) { + LinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) { this.target = target; this.res = res; this.ir = res.getLIR(); this.spillMoveFactory = spillMoveFactory; this.frameMapBuilder = res.getFrameMapBuilder(); this.sortedBlocks = ir.linearScanOrder(); - this.registerAttributes = frameMapBuilder.getRegisterConfig().getAttributesMap(); + this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap(); + this.regAllocConfig = regAllocConfig; this.registers = target.arch.getRegisters(); this.firstVariableNumber = registers.length; @@ -178,7 +180,7 @@ // If all allocatable registers are caller saved, then no registers are live across a call // site. The register allocator can save time not trying to find a register at a call site. - this.callKillsRegisters = this.frameMapBuilder.getRegisterConfig().areAllAllocatableRegistersCallerSaved(); + this.callKillsRegisters = regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved(); } int getFirstLirInstructionId(AbstractBlockBase block) { @@ -1169,7 +1171,7 @@ }; // create a list with all caller-save registers (cpu, fpu, xmm) - Register[] callerSaveRegs = frameMapBuilder.getRegisterConfig().getCallerSaveRegisters(); + Register[] callerSaveRegs = regAllocConfig.getRegisterConfig().getCallerSaveRegisters(); // iterate all blocks in reverse order for (int i = blockCount() - 1; i >= 0; i--) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java Tue Apr 14 14:01:18 2015 +0200 @@ -25,6 +25,7 @@ import java.util.*; import com.oracle.graal.api.code.*; +import com.oracle.graal.compiler.common.alloc.*; import com.oracle.graal.compiler.common.cfg.*; import com.oracle.graal.lir.gen.*; import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory; @@ -34,7 +35,7 @@ @Override protected > void run(TargetDescription target, LIRGenerationResult lirGenRes, List codeEmittingOrder, List linearScanOrder, SpillMoveFactory spillMoveFactory) { - new LinearScan(target, lirGenRes, spillMoveFactory).allocate(); + new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate(); } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java Tue Apr 14 14:01:18 2015 +0200 @@ -713,10 +713,12 @@ Debug.log("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, regUsePos); if (firstUsage <= interval.from() + 1) { - assert false : "cannot spill interval that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + ", interval.from()=" + interval.from(); + String description = "cannot spill interval that is used in first instruction (possible reason: no register found) firstUsage=" + firstUsage + ", interval.from()=" + + interval.from(); // assign a reasonable register and do a bailout in product mode to avoid errors allocator.assignSpillSlot(interval); - throw new BailoutException("LinearScan: no register found"); + allocator.printIntervals(description); + throw new OutOfRegistersException("LinearScan: no register found", description); } splitAndSpillInterval(interval); @@ -766,7 +768,7 @@ } void initVarsForAlloc(Interval interval) { - availableRegs = allocator.frameMapBuilder.getRegisterConfig().getAllocatableRegisters(interval.kind().getPlatformKind()); + availableRegs = allocator.regAllocConfig.getAllocatableRegisters(interval.kind().getPlatformKind()); } static boolean isMove(LIRInstruction op, Interval from, Interval to) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/OutOfRegistersException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/OutOfRegistersException.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.alloc.lsra; + +import com.oracle.graal.api.code.*; + +public class OutOfRegistersException extends BailoutException { + + private static final long serialVersionUID = -3479786650143432195L; + + private final String description; + + public OutOfRegistersException(String msg) { + super(msg); + this.description = ""; + } + + public OutOfRegistersException(Throwable cause, String msg) { + super(cause, msg); + this.description = ""; + } + + public OutOfRegistersException(boolean permanent, String msg) { + super(permanent, msg); + this.description = ""; + } + + public OutOfRegistersException(String msg, String description) { + super(msg); + this.description = description; + } + + public OutOfRegistersException(Throwable cause, String msg, String description) { + super(cause, msg); + this.description = description; + } + + public OutOfRegistersException(boolean permanent, String msg, String description) { + super(permanent, msg); + this.description = description; + } + + public String getDescription() { + return description; + } +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/RegisterVerifier.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/RegisterVerifier.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/RegisterVerifier.java Tue Apr 14 14:01:18 2015 +0200 @@ -225,7 +225,7 @@ op.visitEachInput(useConsumer); // invalidate all caller save registers at calls if (op.destroysCallerSavedRegisters()) { - for (Register r : allocator.frameMapBuilder.getRegisterConfig().getCallerSaveRegisters()) { + for (Register r : allocator.regAllocConfig.getRegisterConfig().getCallerSaveRegisters()) { statePut(inputState, r.asValue(), null); } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/EncodedGraph.java Tue Apr 14 14:01:18 2015 +0200 @@ -41,6 +41,12 @@ private final Assumptions assumptions; private final Set inlinedMethods; + /** + * The "table of contents" of the encoded graph, i.e., the mapping from orderId numbers to the + * offset in the encoded byte[] array. Used as a cache during decoding. + */ + protected long[] nodeStartOffsets; + public EncodedGraph(byte[] encoding, long startOffset, Object[] objects, NodeClass[] types, Assumptions assumptions, Set inlinedMethods) { this.encoding = encoding; this.startOffset = startOffset; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java Tue Apr 14 14:01:18 2015 +0200 @@ -227,11 +227,11 @@ * the stack. */ public FrameState duplicateModifiedDuringCall(int newBci, Kind popKind) { - return duplicateModified(newBci, rethrowException, true, popKind); + return duplicateModified(graph(), newBci, rethrowException, true, popKind); } public FrameState duplicateModifiedBeforeCall(int newBci, Kind popKind, ValueNode... pushedValues) { - return duplicateModified(newBci, rethrowException, false, popKind, pushedValues); + return duplicateModified(graph(), newBci, rethrowException, false, popKind, pushedValues); } /** @@ -241,7 +241,7 @@ * followed by a null slot. */ public FrameState duplicateModified(int newBci, boolean newRethrowException, Kind popKind, ValueNode... pushedValues) { - return duplicateModified(newBci, newRethrowException, duringCall, popKind, pushedValues); + return duplicateModified(graph(), newBci, newRethrowException, duringCall, popKind, pushedValues); } /** @@ -250,7 +250,7 @@ */ public FrameState duplicateModified(Kind popKind, ValueNode pushedValue) { assert pushedValue != null && pushedValue.getKind() == popKind; - return duplicateModified(bci, rethrowException, duringCall, popKind, pushedValue); + return duplicateModified(graph(), bci, rethrowException, duringCall, popKind, pushedValue); } /** @@ -259,7 +259,7 @@ * correctly in slot encoding: a long or double will be followed by a null slot. The bci will be * changed to newBci. */ - private FrameState duplicateModified(int newBci, boolean newRethrowException, boolean newDuringCall, Kind popKind, ValueNode... pushedValues) { + public FrameState duplicateModified(StructuredGraph graph, int newBci, boolean newRethrowException, boolean newDuringCall, Kind popKind, ValueNode... pushedValues) { ArrayList copy; if (newRethrowException && !rethrowException && popKind == Kind.Void) { assert popKind == Kind.Void; @@ -285,7 +285,7 @@ copy.addAll(values.subList(localsSize + stackSize, values.size())); assert checkStackDepth(bci, stackSize, duringCall, rethrowException, newBci, newStackSize, newDuringCall, newRethrowException); - return graph().add(new FrameState(outerFrameState(), method, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings)); + return graph.add(new FrameState(outerFrameState(), method, newBci, copy, localsSize, newStackSize, newRethrowException, newDuringCall, monitorIds, virtualObjectMappings)); } /** diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java Tue Apr 14 14:01:18 2015 +0200 @@ -26,6 +26,7 @@ import java.util.*; +import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.util.*; import com.oracle.graal.debug.*; @@ -70,48 +71,38 @@ protected static class MethodScope { /** The target graph where decoded nodes are added to. */ public final StructuredGraph graph; - /** The state of the caller method. Only non-null during method inlining. */ - public final MethodScope caller; /** The encode graph that is decoded. */ public final EncodedGraph encodedGraph; /** Access to the encoded graph. */ public final TypeReader reader; - /** - * The "table of contents" of the encoded graph, i.e., the mapping from orderId numbers to - * the offset in the encoded byte[] array. - */ - public final long[] nodeStartOffsets; /** The kind of loop explosion to be performed during decoding. */ public final LoopExplosionKind loopExplosion; - /** - * The start node of the decoded graph. This is a temporary node for inlined graphs that - * needs to be deleted after inlining. - */ - public StartNode startNode; /** All return nodes encountered during decoding. */ public final List returnNodes; /** The exception unwind node encountered during decoding, or null. */ public UnwindNode unwindNode; - protected MethodScope(StructuredGraph graph, MethodScope caller, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) { + protected MethodScope(StructuredGraph graph, EncodedGraph encodedGraph, LoopExplosionKind loopExplosion) { this.graph = graph; - this.caller = caller; this.encodedGraph = encodedGraph; this.loopExplosion = loopExplosion; this.returnNodes = new ArrayList<>(); - reader = new UnsafeArrayTypeReader(encodedGraph.getEncoding(), encodedGraph.getStartOffset()); - int nodeCount = reader.getUVInt(); - nodeStartOffsets = new long[nodeCount]; - for (int i = 0; i < nodeCount; i++) { - nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV(); + if (encodedGraph != null) { + reader = new UnsafeArrayTypeReader(encodedGraph.getEncoding(), encodedGraph.getStartOffset()); + if (encodedGraph.nodeStartOffsets == null) { + int nodeCount = reader.getUVInt(); + long[] nodeStartOffsets = new long[nodeCount]; + for (int i = 0; i < nodeCount; i++) { + nodeStartOffsets[i] = encodedGraph.getStartOffset() - reader.getUV(); + } + encodedGraph.nodeStartOffsets = nodeStartOffsets; + } + } else { + reader = null; } } - - public boolean isInlinedMethod() { - return caller != null; - } } /** Decoding state maintained for each loop in the encoded graph. */ @@ -153,7 +144,7 @@ this.iterationStates = null; this.loopBeginOrderId = -1; - int nodeCount = methodScope.nodeStartOffsets.length; + int nodeCount = methodScope.encodedGraph.nodeStartOffsets.length; this.nodesToProcess = new BitSet(nodeCount); this.initialCreatedNodes = new Node[nodeCount]; this.createdNodes = new Node[nodeCount]; @@ -226,22 +217,55 @@ } } + /** + * Additional information encoded for {@link Invoke} nodes to allow method inlining without + * decoding the frame state and successors beforehand. + */ + protected static class InvokeData { + public final Invoke invoke; + public final ResolvedJavaType contextType; + public final int invokeOrderId; + public final int callTargetOrderId; + public final int stateAfterOrderId; + public final int nextOrderId; + + public final int nextNextOrderId; + public final int exceptionOrderId; + public final int exceptionStateOrderId; + public final int exceptionNextOrderId; + + protected InvokeData(Invoke invoke, ResolvedJavaType contextType, int invokeOrderId, int callTargetOrderId, int stateAfterOrderId, int nextOrderId, int nextNextOrderId, int exceptionOrderId, + int exceptionStateOrderId, int exceptionNextOrderId) { + this.invoke = invoke; + this.contextType = contextType; + this.invokeOrderId = invokeOrderId; + this.callTargetOrderId = callTargetOrderId; + this.stateAfterOrderId = stateAfterOrderId; + this.nextOrderId = nextOrderId; + this.nextNextOrderId = nextNextOrderId; + this.exceptionOrderId = exceptionOrderId; + this.exceptionStateOrderId = exceptionStateOrderId; + this.exceptionNextOrderId = exceptionNextOrderId; + } + } + public final void decode(StructuredGraph graph, EncodedGraph encodedGraph) { - MethodScope methodScope = new MethodScope(graph, null, encodedGraph, LoopExplosionKind.NONE); - decode(methodScope); + MethodScope methodScope = new MethodScope(graph, encodedGraph, LoopExplosionKind.NONE); + decode(methodScope, null); cleanupGraph(methodScope); methodScope.graph.verify(); } - protected final void decode(MethodScope methodScope) { + protected final void decode(MethodScope methodScope, FixedWithNextNode startNode) { LoopScope loopScope = new LoopScope(methodScope); - if (methodScope.isInlinedMethod()) { - methodScope.startNode = methodScope.graph.add(new StartNode()); - methodScope.startNode.setNext(makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID)); + FixedNode firstNode; + if (startNode != null) { + firstNode = makeStubNode(methodScope, loopScope, GraphEncoder.FIRST_NODE_ORDER_ID); + startNode.setNext(firstNode); loopScope.nodesToProcess.set(GraphEncoder.FIRST_NODE_ORDER_ID); } else { - methodScope.startNode = methodScope.graph.start(); - registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, methodScope.startNode, false, false); + firstNode = methodScope.graph.start(); + registerNode(loopScope, GraphEncoder.START_NODE_ORDER_ID, firstNode, false, false); loopScope.nodesToProcess.set(GraphEncoder.START_NODE_ORDER_ID); } @@ -262,7 +286,7 @@ if (methodScope.loopExplosion == LoopExplosionKind.MERGE_EXPLODE) { cleanupGraph(methodScope); Debug.dump(methodScope.graph, "Before loop detection"); - detectLoops(methodScope.graph, methodScope.startNode); + detectLoops(methodScope.graph, firstNode); } } @@ -275,6 +299,18 @@ return loopScope; } + if ((node instanceof MergeNode || (node instanceof LoopBeginNode && (methodScope.loopExplosion == LoopExplosionKind.FULL_UNROLL || methodScope.loopExplosion == LoopExplosionKind.FULL_EXPLODE))) && + ((AbstractMergeNode) node).forwardEndCount() == 1) { + AbstractMergeNode merge = (AbstractMergeNode) node; + EndNode singleEnd = merge.forwardEndAt(0); + FixedNode next = makeStubNode(methodScope, loopScope, nodeOrderId + GraphEncoder.BEGIN_NEXT_ORDER_ID_OFFSET); + singleEnd.replaceAtPredecessor(next); + + merge.safeDelete(); + singleEnd.safeDelete(); + return loopScope; + } + LoopScope successorAddScope = loopScope; boolean updatePredecessors = true; if (node instanceof LoopExitNode) { @@ -282,7 +318,7 @@ updatePredecessors = methodScope.loopExplosion == LoopExplosionKind.NONE; } - methodScope.reader.setByteIndex(methodScope.nodeStartOffsets[nodeOrderId]); + methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); int typeId = methodScope.reader.getUVInt(); assert node.getNodeClass() == methodScope.encodedGraph.getNodeClasses()[typeId]; readProperties(methodScope, node); @@ -299,7 +335,7 @@ if (methodScope.loopExplosion != LoopExplosionKind.NONE) { handleLoopExplosionProxyNodes(methodScope, loopScope, (LoopExitNode) node, nodeOrderId); } else { - handleProxyNodes(methodScope, loopScope); + handleProxyNodes(methodScope, loopScope, (LoopExitNode) node); } } else if (node instanceof AbstractEndNode) { @@ -311,7 +347,7 @@ phiNodeScope = loopScope.nextIterations.getLast(); } - int mergeOrderId = methodScope.reader.getUVInt(); + int mergeOrderId = readOrderId(methodScope); AbstractMergeNode merge = (AbstractMergeNode) lookupNode(phiNodeScope, mergeOrderId); if (merge == null) { merge = (AbstractMergeNode) makeStubNode(methodScope, phiNodeScope, mergeOrderId); @@ -332,7 +368,8 @@ handlePhiFunctions(methodScope, phiInputScope, phiNodeScope, (AbstractEndNode) node, merge); } else if (node instanceof Invoke) { - simplifyInvoke(methodScope, loopScope, nodeOrderId, (Invoke) node); + InvokeData invokeData = readInvokeData(methodScope, nodeOrderId, (Invoke) node); + handleInvoke(methodScope, loopScope, invokeData); } else if (node instanceof ReturnNode) { methodScope.returnNodes.add((ReturnNode) node); @@ -347,15 +384,45 @@ return resultScope; } + private InvokeData readInvokeData(MethodScope methodScope, int invokeOrderId, Invoke invoke) { + ResolvedJavaType contextType = (ResolvedJavaType) readObject(methodScope); + int callTargetOrderId = readOrderId(methodScope); + int stateAfterOrderId = readOrderId(methodScope); + int nextOrderId = readOrderId(methodScope); + + if (invoke instanceof InvokeWithExceptionNode) { + int nextNextOrderId = readOrderId(methodScope); + int exceptionOrderId = readOrderId(methodScope); + int exceptionStateOrderId = readOrderId(methodScope); + int exceptionNextOrderId = readOrderId(methodScope); + return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, nextNextOrderId, exceptionOrderId, exceptionStateOrderId, exceptionNextOrderId); + } else { + return new InvokeData(invoke, contextType, invokeOrderId, callTargetOrderId, stateAfterOrderId, nextOrderId, -1, -1, -1, -1); + } + } + /** - * Hook for subclasses. - * - * @param methodScope The current method. - * @param loopScope The current loop. - * @param invokeOrderId The orderId of the method invocation node. - * @param invoke The invocation node. + * {@link Invoke} nodes do not have the {@link CallTargetNode}, {@link FrameState}, and + * successors encoded. Instead, this information is provided separately to allow method inlining + * without decoding and adding them to the graph upfront. For non-inlined methods, this method + * restores the normal state. Subclasses can override it to perform method inlining. */ - protected void simplifyInvoke(MethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) { + protected void handleInvoke(MethodScope methodScope, LoopScope loopScope, InvokeData invokeData) { + assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke"; + CallTargetNode callTarget = (CallTargetNode) ensureNodeCreated(methodScope, loopScope, invokeData.callTargetOrderId); + if (invokeData.invoke instanceof InvokeWithExceptionNode) { + ((InvokeWithExceptionNode) invokeData.invoke).setCallTarget(callTarget); + } else { + ((InvokeNode) invokeData.invoke).setCallTarget(callTarget); + } + + assert invokeData.invoke.stateAfter() == null && invokeData.invoke.stateDuring() == null : "FrameState edges are ignored during decoding of Invoke"; + invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId)); + + invokeData.invoke.setNext(makeStubNode(methodScope, loopScope, invokeData.nextOrderId)); + if (invokeData.invoke instanceof InvokeWithExceptionNode) { + ((InvokeWithExceptionNode) invokeData.invoke).setExceptionEdge((AbstractBeginNode) makeStubNode(methodScope, loopScope, invokeData.exceptionOrderId)); + } } protected void handleLoopExplosionBegin(MethodScope methodScope, LoopScope loopScope, LoopBeginNode loopBegin) { @@ -432,10 +499,14 @@ protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) { } - protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope) { + protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit) { + assert loopExit.stateAfter() == null; + int stateAfterOrderId = readOrderId(methodScope); + loopExit.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, stateAfterOrderId)); + int numProxies = methodScope.reader.getUVInt(); for (int i = 0; i < numProxies; i++) { - int proxyOrderId = methodScope.reader.getUVInt(); + int proxyOrderId = readOrderId(methodScope); ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId); /* * The ProxyNode transports a value from the loop to the outer scope. We therefore @@ -446,9 +517,11 @@ } protected void handleLoopExplosionProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit, int loopExitOrderId) { + assert loopExit.stateAfter() == null; + int stateAfterOrderId = readOrderId(methodScope); + BeginNode begin = methodScope.graph.add(new BeginNode()); - FrameState loopExitState = loopExit.stateAfter(); FixedNode loopExitSuccessor = loopExit.next(); loopExit.replaceAtPredecessor(begin); @@ -457,33 +530,43 @@ * iterations now take the same loop exit, so we have to introduce a new merge node to * handle the merge. */ - MergeNode merge; - if (lookupNode(loopScope.outer, loopExitOrderId) == null) { + MergeNode merge = null; + Node existingExit = lookupNode(loopScope.outer, loopExitOrderId); + if (existingExit == null) { + /* First loop iteration that exits. No merge necessary yet. */ + registerNode(loopScope.outer, loopExitOrderId, begin, false, false); + begin.setNext(loopExitSuccessor); + + } else if (existingExit instanceof BeginNode) { + /* Second loop iteration that exits. Create the merge. */ merge = methodScope.graph.add(new MergeNode()); - registerNode(loopScope.outer, loopExitOrderId, merge, false, false); + registerNode(loopScope.outer, loopExitOrderId, merge, true, false); + /* Add the first iteration. */ + EndNode firstEnd = methodScope.graph.add(new EndNode()); + ((BeginNode) existingExit).setNext(firstEnd); + merge.addForwardEnd(firstEnd); merge.setNext(loopExitSuccessor); + } else { - merge = (MergeNode) loopScope.outer.createdNodes[loopExitOrderId]; + /* Subsequent loop iteration. Merge already created. */ + merge = (MergeNode) existingExit; } - FrameState oldStateAfter = merge.stateAfter(); - merge.setStateAfter(loopExitState); - if (oldStateAfter != null) { - oldStateAfter.safeDelete(); + if (merge != null) { + EndNode end = methodScope.graph.add(new EndNode()); + begin.setNext(end); + merge.addForwardEnd(end); } - EndNode end = methodScope.graph.add(new EndNode()); - begin.setNext(end); - merge.addForwardEnd(end); - /* * Possibly create phi nodes for the original proxy nodes that flow out of the loop. Note * that we definitely do not need a proxy node itself anymore, since the loop was exploded * and is no longer present. */ int numProxies = methodScope.reader.getUVInt(); + boolean phiCreated = false; for (int i = 0; i < numProxies; i++) { - int proxyOrderId = methodScope.reader.getUVInt(); + int proxyOrderId = readOrderId(methodScope); ProxyNode proxy = (ProxyNode) ensureNodeCreated(methodScope, loopScope, proxyOrderId); ValueNode phiInput = proxy.value(); ValueNode replacement; @@ -508,6 +591,7 @@ phi.addInput(phiInput); registerNode(loopScope.outer, proxyOrderId, phi, true, false); replacement = phi; + phiCreated = true; } else { /* Phi node has been created before, so just add the new input. */ @@ -519,9 +603,22 @@ methodScope.graph.replaceFloating(proxy, replacement); } + if (merge != null && (merge.stateAfter() == null || phiCreated)) { + FrameState oldStateAfter = merge.stateAfter(); + registerNode(loopScope.outer, stateAfterOrderId, null, true, true); + merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope.outer, stateAfterOrderId)); + if (oldStateAfter != null) { + oldStateAfter.safeDelete(); + } + } + loopExit.safeDelete(); assert loopExitSuccessor.predecessor() == null; - merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor); + if (merge != null) { + merge.getNodeClass().getSuccessorEdges().update(merge, null, loopExitSuccessor); + } else { + begin.getNodeClass().getSuccessorEdges().update(begin, null, loopExitSuccessor); + } } protected void handlePhiFunctions(MethodScope methodScope, LoopScope phiInputScope, LoopScope phiNodeScope, AbstractEndNode end, AbstractMergeNode merge) { @@ -555,8 +652,8 @@ boolean lazyPhi = !(merge instanceof LoopBeginNode) || methodScope.loopExplosion != LoopExplosionKind.NONE; int numPhis = methodScope.reader.getUVInt(); for (int i = 0; i < numPhis; i++) { - int phiInputOrderId = methodScope.reader.getUVInt(); - int phiNodeOrderId = methodScope.reader.getUVInt(); + int phiInputOrderId = readOrderId(methodScope); + int phiNodeOrderId = readOrderId(methodScope); ValueNode phiInput = (ValueNode) ensureNodeCreated(methodScope, phiInputScope, phiInputOrderId); @@ -588,7 +685,7 @@ } protected Node instantiateNode(MethodScope methodScope, int nodeOrderId) { - methodScope.reader.setByteIndex(methodScope.nodeStartOffsets[nodeOrderId]); + methodScope.reader.setByteIndex(methodScope.encodedGraph.nodeStartOffsets[nodeOrderId]); NodeClass nodeClass = methodScope.encodedGraph.getNodeClasses()[methodScope.reader.getUVInt()]; return nodeClass.allocateInstance(); } @@ -600,8 +697,7 @@ long primitive = methodScope.reader.getSV(); fields.setRawPrimitive(node, pos, primitive); } else { - int objectId = methodScope.reader.getUVInt(); - Object value = methodScope.encodedGraph.getObjects()[objectId]; + Object value = readObject(methodScope); fields.set(node, pos, value); } } @@ -619,7 +715,7 @@ if (skipEdge(node, edges, index, true, true)) { continue; } - int orderId = methodScope.reader.getUVInt(); + int orderId = readOrderId(methodScope); Node value = ensureNodeCreated(methodScope, loopScope, orderId); Edges.initializeNode(node, edges.getOffsets(), index, value); if (updateUsages && value != null && !value.isDeleted()) { @@ -636,7 +732,7 @@ NodeList nodeList = new NodeInputList<>(node, size); Edges.initializeList(node, edges.getOffsets(), index, nodeList); for (int idx = 0; idx < size; idx++) { - int orderId = methodScope.reader.getUVInt(); + int orderId = readOrderId(methodScope); Node value = ensureNodeCreated(methodScope, loopScope, orderId); nodeList.initialize(idx, value); if (updateUsages && value != null && !value.isDeleted()) { @@ -656,17 +752,7 @@ return node; } - long readerByteIndex = methodScope.reader.getByteIndex(); - node = instantiateNode(methodScope, nodeOrderId); - assert !(node instanceof FixedNode); - - /* Read the properties of the node. */ - readProperties(methodScope, node); - /* There must not be any successors to read, since it is a non-fixed node. */ - assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0; - /* Read the inputs of the node, possibly creating them recursively. */ - makeInputNodes(methodScope, loopScope, node, false); - methodScope.reader.setByteIndex(readerByteIndex); + node = decodeFloatingNode(methodScope, loopScope, nodeOrderId); if (node instanceof ProxyNode || node instanceof PhiNode) { /* @@ -688,6 +774,24 @@ } /** + * Decodes a non-fixed node, but does not do any post-processing and does not register it. + */ + protected Node decodeFloatingNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId) { + long readerByteIndex = methodScope.reader.getByteIndex(); + Node node = instantiateNode(methodScope, nodeOrderId); + assert !(node instanceof FixedNode); + + /* Read the properties of the node. */ + readProperties(methodScope, node); + /* There must not be any successors to read, since it is a non-fixed node. */ + assert node.getNodeClass().getEdges(Edges.Type.Successors).getCount() == 0; + /* Read the inputs of the node, possibly creating them recursively. */ + makeInputNodes(methodScope, loopScope, node, false); + methodScope.reader.setByteIndex(readerByteIndex); + return node; + } + + /** * Hook for subclasses to process a non-fixed node before it is added to the graph. * * @param methodScope The current method. @@ -722,7 +826,7 @@ if (skipEdge(node, edges, index, true, true)) { continue; } - int orderId = methodScope.reader.getUVInt(); + int orderId = readOrderId(methodScope); Node value = makeStubNode(methodScope, loopScope, orderId); Edges.initializeNode(node, edges.getOffsets(), index, value); if (updatePredecessors && value != null) { @@ -738,7 +842,7 @@ NodeList nodeList = new NodeSuccessorList<>(node, size); Edges.initializeList(node, edges.getOffsets(), index, nodeList); for (int idx = 0; idx < size; idx++) { - int orderId = methodScope.reader.getUVInt(); + int orderId = readOrderId(methodScope); Node value = makeStubNode(methodScope, loopScope, orderId); nodeList.initialize(idx, value); if (updatePredecessors && value != null) { @@ -792,6 +896,26 @@ assert index == edges.getCount() - 1 : "MergeNode has one variable size input (the ends)"; assert Edges.getNodeList(node, edges.getOffsets(), index) != null : "Input list must have been already created"; return true; + + } else if (node instanceof LoopExitNode && edges.type() == Edges.Type.Inputs && edges.getType(index) == FrameState.class) { + /* The stateAfter of the loop exit is filled manually. */ + return true; + + } else if (node instanceof Invoke) { + assert node instanceof InvokeNode || node instanceof InvokeWithExceptionNode : "The only two Invoke node classes"; + assert direct : "Invoke and InvokeWithException only have direct successor and input edges"; + if (edges.type() == Edges.Type.Successors) { + assert edges.getCount() == (node instanceof InvokeWithExceptionNode ? 2 : 1) : "InvokeNode has one successor (next); InvokeWithExceptionNode has two successors (next, exceptionEdge)"; + return true; + } else { + assert edges.type() == Edges.Type.Inputs; + if (edges.getType(index) == CallTargetNode.class) { + return true; + } else if (edges.getType(index) == FrameState.class) { + assert edges.get(node, index) == null || edges.get(node, index) == ((Invoke) node).stateAfter() : "Only stateAfter can be a FrameState during encoding"; + return true; + } + } } return false; } @@ -807,6 +931,14 @@ loopScope.createdNodes[nodeOrderId] = node; } + protected int readOrderId(MethodScope methodScope) { + return methodScope.reader.getUVInt(); + } + + protected Object readObject(MethodScope methodScope) { + return methodScope.encodedGraph.getObjects()[methodScope.reader.getUVInt()]; + } + /* * The following methods are a literal copy from GraphBuilderPhase. */ diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphEncoder.java Tue Apr 14 14:01:18 2015 +0200 @@ -29,6 +29,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.iterators.*; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; +import com.oracle.graal.nodes.java.*; /** * Encodes a {@link StructuredGraph} to a compact byte[] array. All nodes of the graph and edges @@ -100,6 +101,12 @@ public static final int FIRST_NODE_ORDER_ID = 2; /** + * The known offset between the orderId of a {@link AbstractBeginNode} and its + * {@link AbstractBeginNode#next() successor}. + */ + protected static final int BEGIN_NEXT_ORDER_ID_OFFSET = 1; + + /** * Collects all non-primitive data referenced from nodes. The encoding uses an index into an * array for decoding. Because of the variable-length encoding, it is beneficial that frequently * used objects have the small indices. @@ -148,6 +155,9 @@ objects.addObject(nodeClass.getData().get(node, i)); } } + if (node instanceof Invoke) { + objects.addObject(((Invoke) node).getContextType()); + } } } @@ -182,7 +192,10 @@ long[] nodeStartOffsets = new long[nodeCount]; for (Map.Entry entry : nodeOrder.orderIds.entries()) { Node node = entry.getKey(); - nodeStartOffsets[entry.getValue()] = writer.getBytesWritten(); + Integer orderId = entry.getValue(); + + assert !(node instanceof AbstractBeginNode) || nodeOrder.orderIds.get(((AbstractBeginNode) node).next()) == orderId + BEGIN_NEXT_ORDER_ID_OFFSET; + nodeStartOffsets[orderId] = writer.getBytesWritten(); /* Write out the type, properties, and edges. */ NodeClass nodeClass = node.getNodeClass(); @@ -213,11 +226,30 @@ } else if (node instanceof LoopExitNode) { LoopExitNode exit = (LoopExitNode) node; + writeOrderId(exit.stateAfter(), nodeOrder); /* Write all proxy nodes of the LoopExitNode. */ writer.putUV(exit.proxies().count()); for (ProxyNode proxy : exit.proxies()) { writeOrderId(proxy, nodeOrder); } + + } else if (node instanceof Invoke) { + Invoke invoke = (Invoke) node; + assert invoke.stateDuring() == null : "stateDuring is not used in high-level graphs"; + + writeObjectId(invoke.getContextType()); + writeOrderId(invoke.callTarget(), nodeOrder); + writeOrderId(invoke.stateAfter(), nodeOrder); + writeOrderId(invoke.next(), nodeOrder); + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithExcpetion = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExcpetion.exceptionEdge(); + + writeOrderId(invokeWithExcpetion.next().next(), nodeOrder); + writeOrderId(invokeWithExcpetion.exceptionEdge(), nodeOrder); + writeOrderId(exceptionEdge.stateAfter(), nodeOrder); + writeOrderId(exceptionEdge.next(), nodeOrder); + } } } @@ -252,13 +284,8 @@ FixedNode current = graph.start(); do { add(current); - if (current instanceof InvokeWithExceptionNode) { - /* - * Special handling for invokes: the orderID of the invocation, the regular - * successor, and the exception edge must be consecutive. - */ - add(((InvokeWithExceptionNode) current).next()); - add(((InvokeWithExceptionNode) current).exceptionEdge()); + if (current instanceof AbstractBeginNode) { + add(((AbstractBeginNode) current).next()); } if (current instanceof FixedWithNextNode) { @@ -308,7 +335,7 @@ writer.putSV(primitive); } else { Object property = fields.get(node, idx); - writer.putUV(objects.getIndex(property)); + writeObjectId(property); } } } @@ -343,6 +370,10 @@ writer.putUV(node == null ? NULL_ORDER_ID : nodeOrder.orderIds.get(node)); } + protected void writeObjectId(Object object) { + writer.putUV(objects.getIndex(object)); + } + /** * Verification code that checks that the decoding of an encode graph is the same as the * original graph. @@ -365,7 +396,7 @@ pushToWorklist(expectedGraph.start(), actualGraph.start(), nodeMapping, workList); while (!workList.isEmpty()) { - Pair pair = workList.pop(); + Pair pair = workList.removeFirst(); Node expectedNode = pair.first; Node actualNode = pair.second; assert expectedNode.getClass() == actualNode.getClass(); @@ -377,7 +408,7 @@ /* The order of the ends can be different, so ignore them. */ verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, true); } else if (expectedNode instanceof PhiNode) { - /* The order of phi inputs can be different, so they are checked manually below. */ + verifyPhi((PhiNode) expectedNode, (PhiNode) actualNode, nodeMapping, workList); } else { verifyNodesEqual(expectedNode.inputs(), actualNode.inputs(), nodeMapping, workList, false); } @@ -413,20 +444,34 @@ return true; } + protected static void verifyPhi(PhiNode expectedPhi, PhiNode actualPhi, NodeMap nodeMapping, Deque> workList) { + AbstractMergeNode expectedMergeNode = expectedPhi.merge(); + AbstractMergeNode actualMergeNode = actualPhi.merge(); + assert actualMergeNode == nodeMapping.get(expectedMergeNode); + + for (EndNode expectedEndNode : expectedMergeNode.ends) { + EndNode actualEndNode = (EndNode) nodeMapping.get(expectedEndNode); + if (actualEndNode != null) { + ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode); + ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode); + verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false); + } + } + } + protected static void verifyPhis(AbstractEndNode expectedEndNode, AbstractEndNode actualEndNode, NodeMap nodeMapping, Deque> workList) { AbstractMergeNode expectedMergeNode = expectedEndNode.merge(); AbstractMergeNode actualMergeNode = (AbstractMergeNode) nodeMapping.get(expectedMergeNode); - - Iterator actualPhis = actualMergeNode.phis().iterator(); - for (PhiNode expectedPhi : expectedMergeNode.phis()) { - PhiNode actualPhi = actualPhis.next(); - verifyNodeEqual(expectedPhi, actualPhi, nodeMapping, workList, false); + assert actualMergeNode != null; - ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode); - ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode); - verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false); + for (PhiNode expectedPhi : expectedMergeNode.phis()) { + PhiNode actualPhi = (PhiNode) nodeMapping.get(expectedPhi); + if (actualPhi != null) { + ValueNode expectedPhiInput = expectedPhi.valueAt(expectedEndNode); + ValueNode actualPhiInput = actualPhi.valueAt(actualEndNode); + verifyNodeEqual(expectedPhiInput, actualPhiInput, nodeMapping, workList, false); + } } - assert !actualPhis.hasNext(); } private static void verifyNodesEqual(NodeIterable expectedNodes, NodeIterable actualNodes, NodeMap nodeMapping, Deque> workList, boolean ignoreEndNode) { @@ -453,7 +498,12 @@ protected static void pushToWorklist(Node expectedNode, Node actualNode, NodeMap nodeMapping, Deque> workList) { nodeMapping.set(expectedNode, actualNode); - workList.push(new Pair<>(expectedNode, actualNode)); + if (expectedNode instanceof AbstractEndNode) { + /* To ensure phi nodes have been added, we handle everything before block ends. */ + workList.addLast(new Pair<>(expectedNode, actualNode)); + } else { + workList.addFirst(new Pair<>(expectedNode, actualNode)); + } } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -107,7 +107,7 @@ @Override public Node canonical(CanonicalizerTool tool) { - if (stamp() == StampFactory.illegal(object.getKind())) { + if (stamp().isIllegal()) { // The guard always fails return new DeoptimizeNode(action, reason); } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -140,6 +140,15 @@ return super.verify(); } + public void eliminateNegation() { + AbstractBeginNode oldTrueSuccessor = trueSuccessor; + AbstractBeginNode oldFalseSuccessor = falseSuccessor; + trueSuccessor = oldFalseSuccessor; + falseSuccessor = oldTrueSuccessor; + trueSuccessorProbability = 1 - trueSuccessorProbability; + setCondition(((LogicNegationNode) condition).getValue()); + } + @Override public void simplify(SimplifierTool tool) { if (trueSuccessor().next() instanceof DeoptimizeNode) { @@ -155,15 +164,7 @@ } if (condition() instanceof LogicNegationNode) { - AbstractBeginNode trueSucc = trueSuccessor(); - AbstractBeginNode falseSucc = falseSuccessor(); - setTrueSuccessor(null); - setFalseSuccessor(null); - LogicNegationNode negation = (LogicNegationNode) condition(); - IfNode newIfNode = graph().add(new IfNode(negation.getValue(), falseSucc, trueSucc, 1 - trueSuccessorProbability)); - predecessor().replaceFirstSuccessor(this, newIfNode); - GraphUtil.killWithUnusedFloatingInputs(this); - return; + eliminateNegation(); } if (condition() instanceof LogicConstantNode) { LogicConstantNode c = (LogicConstantNode) condition(); diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -64,6 +64,11 @@ return callTarget; } + void setCallTarget(CallTargetNode callTarget) { + updateUsages(this.callTarget, callTarget); + this.callTarget = callTarget; + } + @Override public boolean isPolymorphic() { return polymorphic; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -81,6 +81,11 @@ return callTarget; } + void setCallTarget(CallTargetNode callTarget) { + updateUsages(this.callTarget, callTarget); + this.callTarget = callTarget; + } + public MethodCallTargetNode methodCallTarget() { return (MethodCallTargetNode) callTarget; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.nodes.calc; +import java.util.*; + import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.calc.*; import com.oracle.graal.compiler.common.type.*; @@ -43,8 +45,16 @@ protected final FloatConvert op; + private static final EnumMap> getOps; + static { + getOps = new EnumMap<>(FloatConvert.class); + for (FloatConvert op : FloatConvert.values()) { + getOps.put(op, table -> table.getFloatConvert(op)); + } + } + public FloatConvertNode(FloatConvert op, ValueNode input) { - super(TYPE, table -> table.getFloatConvert(op), input); + super(TYPE, getOps.get(op), input); this.op = op; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -101,7 +101,7 @@ * This really represent a misuse of LoadMethod since we're loading from a class which * isn't known to implement the original method but for now at least fold it away. */ - return ConstantNode.forConstant(JavaConstant.NULL_POINTER, null); + return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, null); } else { return ConstantNode.forConstant(stamp(), newMethod.getEncoding(), tool.getMetaAccess()); } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -116,7 +116,7 @@ Stamp newStamp = StampFactory.declaredTrusted(type).improveWith(object().stamp()); ValueNode condition; ValueNode theValue = object; - if (newStamp instanceof IllegalStamp) { + if (newStamp.isIllegal()) { // This is a check cast that will always fail condition = LogicConstantNode.contradiction(graph()); newStamp = StampFactory.declaredTrusted(type); diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java Tue Apr 14 14:01:18 2015 +0200 @@ -150,11 +150,11 @@ public static void killWithUnusedFloatingInputs(Node node) { node.safeDelete(); - for (Node in : node.inputs()) { + node.acceptInputs((n, in) -> { if (in.isAlive() && in.hasNoUsages() && !(in instanceof FixedNode)) { killWithUnusedFloatingInputs(in); } - } + }); } public static void removeFixedWithUnusedInputs(FixedWithNextNode fixed) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Tue Apr 14 14:01:18 2015 +0200 @@ -306,7 +306,7 @@ if (callerLockDepth != 0) { for (MonitorIdNode original : inlineGraph.getNodes(MonitorIdNode.TYPE)) { MonitorIdNode monitor = (MonitorIdNode) duplicates.get(original); - processMonitorId(invoke, monitor); + processMonitorId(invoke.stateAfter(), monitor); } } } else { @@ -432,8 +432,7 @@ return pos; } - public static void processMonitorId(Invoke invoke, MonitorIdNode monitorIdNode) { - FrameState stateAfter = invoke.stateAfter(); + public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) { if (stateAfter != null) { int callerLockDepth = stateAfter.nestedLockDepth(); monitorIdNode.setLockDepth(monitorIdNode.getLockDepth() + callerLockDepth); @@ -579,26 +578,43 @@ } public static ValueNode mergeReturns(AbstractMergeNode merge, List returnNodes, List canonicalizedNodes) { + ValueNode singleReturnValue = null; PhiNode returnValuePhi = null; - for (ReturnNode returnNode : returnNodes) { - // create and wire up a new EndNode - EndNode endNode = merge.graph().add(new EndNode()); - merge.addForwardEnd(endNode); + if (returnNode.result() != null) { + if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == returnNode.result())) { + /* Only one return value, so no need yet for a phi node. */ + singleReturnValue = returnNode.result(); - if (returnNode.result() != null) { - if (returnValuePhi == null) { + } else if (returnValuePhi == null) { + /* Found a second return value, so create phi node. */ returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge)); if (canonicalizedNodes != null) { canonicalizedNodes.add(returnValuePhi); } + for (int i = 0; i < merge.forwardEndCount(); i++) { + returnValuePhi.addInput(singleReturnValue); + } + returnValuePhi.addInput(returnNode.result()); + + } else { + /* Multiple return values, just add to existing phi node. */ + returnValuePhi.addInput(returnNode.result()); } - returnValuePhi.addInput(returnNode.result()); } + + // create and wire up a new EndNode + EndNode endNode = merge.graph().add(new EndNode()); + merge.addForwardEnd(endNode); returnNode.replaceAndDelete(endNode); - } - return returnValuePhi; + + if (returnValuePhi != null) { + assert returnValuePhi.verify(); + return returnValuePhi; + } else { + return singleReturnValue; + } } private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map duplicates) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/InferStamps.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/InferStamps.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/InferStamps.java Tue Apr 14 14:01:18 2015 +0200 @@ -88,7 +88,7 @@ for (Node n : graph.getNodes()) { if (n instanceof ValuePhiNode) { ValueNode node = (ValueNode) n; - assert !(node.stamp() instanceof IllegalStamp) : "Stamp is illegal after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion)."; + assert node.stamp().isLegal() : "Stamp is illegal after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion)."; } } return true; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java --- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64GraphBuilderPlugins.java Tue Apr 14 14:01:18 2015 +0200 @@ -29,10 +29,10 @@ import com.oracle.graal.amd64.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.replacements.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java Tue Apr 14 14:01:18 2015 +0200 @@ -30,7 +30,6 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java Tue Apr 14 14:01:18 2015 +0200 @@ -27,7 +27,7 @@ import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java Tue Apr 14 14:01:18 2015 +0200 @@ -47,8 +47,6 @@ import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement; -import com.oracle.graal.java.AbstractBytecodeParser.IntrinsicContext; -import com.oracle.graal.java.AbstractBytecodeParser.ReplacementContext; import com.oracle.graal.java.*; import com.oracle.graal.java.GraphBuilderPhase.Instance; import com.oracle.graal.nodes.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java --- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java Tue Apr 14 14:01:18 2015 +0200 @@ -23,7 +23,6 @@ package com.oracle.graal.replacements; import static com.oracle.graal.api.code.MemoryBarriers.*; -import static com.oracle.graal.compiler.common.GraalOptions.*; import java.lang.reflect.*; import java.util.*; @@ -38,8 +37,8 @@ import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.graph.*; import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.debug.*; @@ -97,23 +96,17 @@ } private static void registerStringPlugins(InvocationPlugins plugins) { - /* - * AMD64's String.equals substitution needs about 8 registers so we disable it if there is - * some artificial register pressure. - */ - if (RegisterPressure.getValue() == null) { - Registration r = new Registration(plugins, String.class); - r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class); + Registration r = new Registration(plugins, String.class); + r.registerMethodSubstitution(StringSubstitutions.class, "equals", Receiver.class, Object.class); - r = new Registration(plugins, StringSubstitutions.class); - r.register1("getValue", String.class, new InvocationPlugin() { - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD); - b.addPush(new LoadFieldNode(value, field)); - return true; - } - }); - } + r = new Registration(plugins, StringSubstitutions.class); + r.register1("getValue", String.class, new InvocationPlugin() { + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { + ResolvedJavaField field = b.getMetaAccess().lookupJavaField(STRING_VALUE_FIELD); + b.addPush(new LoadFieldNode(value, field)); + return true; + } + }); } private static void registerArraysPlugins(InvocationPlugins plugins) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -67,7 +67,7 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument"); + Instrument instrument = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument"); probe.attach(instrument); assertPartialEvalEquals("constant42", root); } @@ -79,7 +79,7 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument"); + Instrument instrument = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument"); probe.attach(instrument); assertPartialEvalEquals("constant42", root); } @@ -91,7 +91,7 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument"); + Instrument instrument = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument"); probe.attach(instrument); instrument.dispose(); assertPartialEvalEquals("constant42", root); @@ -104,7 +104,7 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument"); + Instrument instrument = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument"); probe.attach(instrument); instrument.dispose(); assertPartialEvalEquals("constant42", root); @@ -117,9 +117,9 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument1 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 1"); + Instrument instrument1 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 1"); probe.attach(instrument1); - Instrument instrument2 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 2"); + Instrument instrument2 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 2"); probe.attach(instrument2); assertPartialEvalEquals("constant42", root); } @@ -131,9 +131,9 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument1 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 1"); + Instrument instrument1 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 1"); probe.attach(instrument1); - Instrument instrument2 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 2"); + Instrument instrument2 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 2"); probe.attach(instrument2); assertPartialEvalEquals("constant42", root); } @@ -145,11 +145,11 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument1 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 1"); + Instrument instrument1 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 1"); probe.attach(instrument1); - Instrument instrument2 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 2"); + Instrument instrument2 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 2"); probe.attach(instrument2); - Instrument instrument3 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 3"); + Instrument instrument3 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 3"); probe.attach(instrument3); assertPartialEvalEquals("constant42", root); } @@ -161,11 +161,11 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument1 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 1"); + Instrument instrument1 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 1"); probe.attach(instrument1); - Instrument instrument2 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 2"); + Instrument instrument2 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 2"); probe.attach(instrument2); - Instrument instrument3 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 3"); + Instrument instrument3 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 3"); probe.attach(instrument3); assertPartialEvalEquals("constant42", root); } @@ -177,11 +177,11 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument1 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 1"); + Instrument instrument1 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 1"); probe.attach(instrument1); - Instrument instrument2 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 2"); + Instrument instrument2 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 2"); probe.attach(instrument2); - Instrument instrument3 = Instrument.create(new DefaultInstrumentListener(), "Null test Instrument 3"); + Instrument instrument3 = Instrument.create(new DefaultSimpleInstrumentListener(), "Null test Instrument 3"); probe.attach(instrument3); instrument2.dispose(); assertPartialEvalEquals("constant42", root); @@ -194,54 +194,56 @@ RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - Instrument instrument1 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 1"); + Instrument instrument1 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 1"); probe.attach(instrument1); - Instrument instrument2 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 2"); + Instrument instrument2 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 2"); probe.attach(instrument2); - Instrument instrument3 = Instrument.create(new DefaultASTInstrumentListener(), "Null test Instrument 3"); + Instrument instrument3 = Instrument.create(new DefaultStandardInstrumentListener(), "Null test Instrument 3"); probe.attach(instrument3); instrument2.dispose(); assertPartialEvalEquals("constant42", root); } @Test - public void constantValueInertToolNodeInstrumentListener() { + public void constantValueInertSpliceInstrumentListener() { FrameDescriptor fd = new FrameDescriptor(); AbstractTestNode result = new ConstantTestNode(42); RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - // A listener that could insert a "tool node" into the AST, but which never does. - Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + // A listener that could insert a SplicedNode into the AST, but which never does. + Instrument instrument = Instrument.create(new SpliceInstrumentListener() { - public ToolNode getToolNode(Probe p) { + public SplicedNode getSpliceNode(Probe p) { return null; } }, null); probe.attach(instrument); + // It all gets compiled away assertPartialEvalEquals("constant42", root); } @Test - public void constantValueInertToolNode() { + public void constantValueInertSplicedNode() { FrameDescriptor fd = new FrameDescriptor(); AbstractTestNode result = new ConstantTestNode(42); RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - // A listener that inserts a "tool node" with empty methods into the AST. - Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + // A listener that inserts a SplicedNode with empty methods into the AST. + Instrument instrument = Instrument.create(new SpliceInstrumentListener() { - public ToolNode getToolNode(Probe p) { - return new ToolNode() { + public SplicedNode getSpliceNode(Probe p) { + return new SplicedNode() { }; } }, null); probe.attach(instrument); + // It all gets compiled away. assertPartialEvalEquals("constant42", root); } @@ -280,7 +282,7 @@ Assert.assertEquals(0, count[0]); // Didn't count anything // Add a counting instrument; this changes the "Probe state" and should cause a deopt - final Instrument countingInstrument = Instrument.create(new DefaultInstrumentListener() { + final Instrument countingInstrument = Instrument.create(new DefaultSimpleInstrumentListener() { @Override public void enter(Probe p) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PEGraphDecoder.java Tue Apr 14 14:01:18 2015 +0200 @@ -29,13 +29,14 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; +import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.graphbuilderconf.*; import com.oracle.graal.graphbuilderconf.InlineInvokePlugin.InlineInfo; import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; import com.oracle.graal.java.*; +import com.oracle.graal.nodeinfo.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.extended.*; @@ -63,8 +64,11 @@ protected final StampProvider stampProvider; protected class PEMethodScope extends MethodScope { + /** The state of the caller method. Only non-null during method inlining. */ + protected final PEMethodScope caller; + protected final LoopScope callerLoopScope; protected final ResolvedJavaMethod method; - protected final Invoke invoke; + protected final InvokeData invokeData; protected final int inliningDepth; protected final LoopExplosionPlugin loopExplosionPlugin; @@ -73,15 +77,20 @@ protected final ParameterPlugin parameterPlugin; protected final ValueNode[] arguments; - protected FrameState outerFrameState; + protected FrameState outerState; + protected FrameState exceptionState; + protected ExceptionPlaceholderNode exceptionPlaceholderNode; protected BytecodePosition bytecodePosition; - protected PEMethodScope(StructuredGraph targetGraph, MethodScope caller, EncodedGraph encodedGraph, ResolvedJavaMethod method, Invoke invoke, int inliningDepth, - LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin, ValueNode[] arguments) { - super(targetGraph, caller, encodedGraph, loopExplosionKind(method, loopExplosionPlugin)); + protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData, + int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin, + ValueNode[] arguments) { + super(targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin)); + this.caller = caller; + this.callerLoopScope = callerLoopScope; this.method = method; - this.invoke = invoke; + this.invokeData = invokeData; this.inliningDepth = inliningDepth; this.loopExplosionPlugin = loopExplosionPlugin; this.invocationPlugins = invocationPlugins; @@ -89,6 +98,10 @@ this.parameterPlugin = parameterPlugin; this.arguments = arguments; } + + public boolean isInlinedMethod() { + return caller != null; + } } protected class PECanonicalizerTool implements CanonicalizerTool { @@ -114,7 +127,7 @@ } protected class PENonAppendGraphBuilderContext implements GraphBuilderContext { - private final PEMethodScope methodScope; + protected final PEMethodScope methodScope; public PENonAppendGraphBuilderContext(PEMethodScope methodScope) { this.methodScope = methodScope; @@ -212,13 +225,11 @@ } protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext { - protected final Invoke invoke; protected FixedWithNextNode lastInstr; protected ValueNode pushedNode; - public PEAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke, FixedWithNextNode lastInstr) { + public PEAppendGraphBuilderContext(PEMethodScope methodScope, FixedWithNextNode lastInstr) { super(methodScope); - this.invoke = invoke; this.lastInstr = lastInstr; } @@ -232,7 +243,9 @@ @Override public FrameState createStateAfter() { - return invoke.stateAfter().duplicate(); + Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); + getGraph().add(stateAfter); + return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter); } @Override @@ -274,6 +287,15 @@ } } + @NodeInfo + static class ExceptionPlaceholderNode extends ValueNode { + public static final NodeClass TYPE = NodeClass.create(ExceptionPlaceholderNode.class); + + public ExceptionPlaceholderNode() { + super(TYPE, StampFactory.object()); + } + } + public PEGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, StampProvider stampProvider) { this.metaAccess = metaAccess; this.constantReflection = constantReflection; @@ -294,8 +316,9 @@ public void decode(StructuredGraph targetGraph, ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, InlineInvokePlugin inlineInvokePlugin, ParameterPlugin parameterPlugin) { - PEMethodScope methodScope = new PEMethodScope(targetGraph, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin, parameterPlugin, null); - decode(methodScope); + PEMethodScope methodScope = new PEMethodScope(targetGraph, null, null, lookupEncodedGraph(method), method, null, 0, loopExplosionPlugin, invocationPlugins, inlineInvokePlugin, + parameterPlugin, null); + decode(methodScope, null); cleanupGraph(methodScope); methodScope.graph.verify(); } @@ -310,8 +333,6 @@ protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) { PEMethodScope methodScope = (PEMethodScope) s; - Debug.dump(methodScope.graph, "Loop iteration " + loopScope.loopIteration); - if (loopScope.loopIteration > MaximumLoopExplosionCount.getValue()) { String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?"; if (FailedLoopExplosionIsFatal.getValue()) { @@ -323,39 +344,52 @@ } @Override - protected void simplifyInvoke(MethodScope s, LoopScope loopScope, int invokeOrderId, Invoke invoke) { - if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { - return; - } + protected void handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) { PEMethodScope methodScope = (PEMethodScope) s; - MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + + /* + * Decode the call target, but do not add it to the graph yet. This avoids adding usages for + * all the arguments, which are expensive to remove again when we can inline the method. + */ + assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke"; + CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId); + if (!(callTarget instanceof MethodCallTargetNode) || !trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget)) { + /* We know that we need an invoke, so now we can add the call target to the graph. */ + methodScope.graph.add(callTarget); + registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false); + super.handleInvoke(methodScope, loopScope, invokeData); + } + } + + protected boolean trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { // attempt to devirtualize the call - ResolvedJavaType contextType = (invoke.stateAfter() == null && invoke.stateDuring() == null) ? null : invoke.getContextType(); - ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), contextType); + ResolvedJavaMethod specialCallTarget = MethodCallTargetNode.findSpecialCallTarget(callTarget.invokeKind(), callTarget.receiver(), callTarget.targetMethod(), invokeData.contextType); if (specialCallTarget != null) { callTarget.setTargetMethod(specialCallTarget); callTarget.setInvokeKind(InvokeKind.Special); } - if (tryInvocationPlugin(methodScope, loopScope, invokeOrderId, invoke)) { - return; + if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) { + return true; } - if (tryInline(methodScope, loopScope, invokeOrderId, invoke)) { - return; + if (tryInline(methodScope, loopScope, invokeData, callTarget)) { + return true; } if (methodScope.inlineInvokePlugin != null) { - methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope), callTarget.targetMethod(), invoke); + methodScope.inlineInvokePlugin.notifyOfNoninlinedInvoke(new PENonAppendGraphBuilderContext(methodScope), callTarget.targetMethod(), invokeData.invoke); } + return false; } - protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) { + protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { if (methodScope.invocationPlugins == null) { return false; } - MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + Invoke invoke = invokeData.invoke; + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); InvocationPlugin invocationPlugin = methodScope.invocationPlugins.lookupInvocation(targetMethod); if (invocationPlugin == null) { @@ -364,52 +398,41 @@ ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor(); - FixedNode invokeNext = invoke.next(); - AbstractBeginNode invokeException = null; - if (invoke instanceof InvokeWithExceptionNode) { - invokeException = ((InvokeWithExceptionNode) invoke).exceptionEdge(); - } + /* Remove invoke from graph so that invocation plugin can append nodes to the predecessor. */ invoke.asNode().replaceAtPredecessor(null); - PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, invoke, invokePredecessor); + PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin, + methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); + PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor); InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext); + if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) { if (graphBuilderContext.lastInstr != null) { - registerNode(loopScope, invokeOrderId, graphBuilderContext.pushedNode, true, true); + registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true); invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode); + graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData)); } else { assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?"; invoke.asNode().replaceAtUsages(null); } deleteInvoke(invoke); - if (invokeException != null) { - invokeException.safeDelete(); - } - - if (graphBuilderContext.lastInstr != null) { - graphBuilderContext.lastInstr.setNext(invokeNext); - } else { - invokeNext.replaceAtPredecessor(null); - invokeNext.safeDelete(); - } return true; } else { - /* Restore original state: invoke is in Graph. */ + /* Intrinsification failed, restore original state: invoke is in Graph. */ invokePredecessor.setNext(invoke.asNode()); return false; } } - protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, int invokeOrderId, Invoke invoke) { - if (methodScope.inlineInvokePlugin == null || !invoke.getInvokeKind().isDirect()) { + protected boolean tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { + if (methodScope.inlineInvokePlugin == null || !callTarget.invokeKind().isDirect()) { return false; } - MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); ResolvedJavaMethod targetMethod = callTarget.targetMethod(); if (!targetMethod.canBeInlined()) { return false; @@ -429,50 +452,72 @@ return false; } - int exceptionObjectOrderId = -1; - if (invoke instanceof InvokeWithExceptionNode) { - /* - * We need to have the regular next node (usually a KillingBeginNode) and the exception - * edge node (always an ExceptionObjectNode) fully decoded, because both can be changed - * or replaced as part of the inlining process. The GraphEncoder adds these two - * successors in a known order (first the regular next node, then the exception edge) - * that we can rely on here. - */ - assert ((InvokeWithExceptionNode) invoke).next().next() == null; - processNextNode(methodScope, loopScope); - assert ((InvokeWithExceptionNode) invoke).next().next() != null; + Invoke invoke = invokeData.invoke; + FixedNode invokeNode = invoke.asNode(); + FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor(); + invokeNode.replaceAtPredecessor(null); - assert ((InvokeWithExceptionNode) invoke).exceptionEdge().next() == null; - exceptionObjectOrderId = loopScope.nodesToProcess.nextSetBit(0); - processNextNode(methodScope, loopScope); - assert ((InvokeWithExceptionNode) invoke).exceptionEdge().next() != null; - } - - PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, graphToInline, inlineMethod, invoke, methodScope.inliningDepth + 1, methodScope.loopExplosionPlugin, - methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); + PEMethodScope inlineScope = new PEMethodScope(methodScope.graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1, + methodScope.loopExplosionPlugin, methodScope.invocationPlugins, methodScope.inlineInvokePlugin, null, arguments); /* Do the actual inlining by decoding the inlineMethod */ - decode(inlineScope); + decode(inlineScope, predecessor); ValueNode exceptionValue = null; if (inlineScope.unwindNode != null) { exceptionValue = inlineScope.unwindNode.exception(); } + UnwindNode unwindNode = inlineScope.unwindNode; - FixedNode firstInlinedNode = inlineScope.startNode.next(); - /* The StartNode was only necessary as a placeholder during decoding. */ - inlineScope.startNode.safeDelete(); + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); + assert invokeWithException.next() == null; + assert invokeWithException.exceptionEdge() == null; + + if (unwindNode != null) { + assert unwindNode.predecessor() != null; + Node n = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId); + unwindNode.replaceAndDelete(n); + } + + } else { + if (unwindNode != null && !unwindNode.isDeleted()) { + DeoptimizeNode deoptimizeNode = methodScope.graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); + unwindNode.replaceAndDelete(deoptimizeNode); + } + } + + assert invoke.next() == null; - assert inlineScope.startNode.stateAfter() == null; - ValueNode returnValue = InliningUtil.finishInlining(invoke, methodScope.graph, firstInlinedNode, inlineScope.returnNodes, inlineScope.unwindNode, inlineScope.encodedGraph.getAssumptions(), - inlineScope.encodedGraph.getInlinedMethods(), null); + ValueNode returnValue; + List returnNodes = inlineScope.returnNodes; + if (!returnNodes.isEmpty()) { + FixedNode n; + n = nodeAfterInvoke(methodScope, loopScope, invokeData); + if (returnNodes.size() == 1) { + ReturnNode returnNode = returnNodes.get(0); + returnValue = returnNode.result(); + returnNode.replaceAndDelete(n); + } else { + AbstractMergeNode merge = methodScope.graph.add(new MergeNode()); + merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId)); + returnValue = InliningUtil.mergeReturns(merge, returnNodes, null); + merge.setNext(n); + } + } else { + returnValue = null; + } + invokeNode.replaceAtUsages(returnValue); /* * Usage the handles that we have on the return value and the exception to update the * orderId->Node table. */ - registerNode(loopScope, invokeOrderId, returnValue, true, true); + registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true); if (invoke instanceof InvokeWithExceptionNode) { - registerNode(loopScope, exceptionObjectOrderId, exceptionValue, true, true); + registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true); + } + if (inlineScope.exceptionPlaceholderNode != null) { + inlineScope.exceptionPlaceholderNode.replaceAndDelete(exceptionValue); } deleteInvoke(invoke); @@ -480,6 +525,16 @@ return true; } + public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData) { + FixedNode n; + if (invokeData.invoke instanceof InvokeWithExceptionNode) { + n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId); + } else { + n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId); + } + return n; + } + private static void deleteInvoke(Invoke invoke) { /* * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can @@ -487,7 +542,7 @@ */ FrameState frameState = invoke.stateAfter(); invoke.asNode().safeDelete(); - invoke.callTarget().safeDelete(); + assert invoke.callTarget() == null : "must not have been added to the graph yet"; if (frameState != null && frameState.hasNoUsages()) { frameState.safeDelete(); } @@ -499,15 +554,20 @@ protected void simplifyFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) { PEMethodScope methodScope = (PEMethodScope) s; - if (node instanceof IfNode && ((IfNode) node).condition() instanceof LogicConstantNode) { + if (node instanceof IfNode) { IfNode ifNode = (IfNode) node; - boolean condition = ((LogicConstantNode) ifNode.condition()).getValue(); - AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition); - AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition); + if (ifNode.condition() instanceof LogicNegationNode) { + ifNode.eliminateNegation(); + } + if (ifNode.condition() instanceof LogicConstantNode) { + boolean condition = ((LogicConstantNode) ifNode.condition()).getValue(); + AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition); + AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition); - methodScope.graph.removeSplit(ifNode, survivingSuccessor); - assert deadSuccessor.next() == null : "must not be parsed yet"; - deadSuccessor.safeDelete(); + methodScope.graph.removeSplit(ifNode, survivingSuccessor); + assert deadSuccessor.next() == null : "must not be parsed yet"; + deadSuccessor.safeDelete(); + } } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) { IntegerSwitchNode switchNode = (IntegerSwitchNode) node; @@ -587,31 +647,68 @@ return node; } + protected void ensureOuterStateDecoded(PEMethodScope methodScope) { + if (methodScope.outerState == null && methodScope.caller != null) { + FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter(); + if (stateAtReturn == null) { + stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); + } + + Kind invokeReturnKind = methodScope.invokeData.invoke.asNode().getKind(); + FrameState outerState = stateAtReturn.duplicateModified(methodScope.graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind); + + if (methodScope.caller != null) { + ensureOuterStateDecoded(methodScope.caller); + outerState.setOuterFrameState(methodScope.caller.outerState); + } + methodScope.outerState = outerState; + } + } + + protected void ensureStateAfterDecoded(PEMethodScope methodScope) { + if (methodScope.invokeData.invoke.stateAfter() == null) { + methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId)); + } + } + + protected void ensureExceptionStateDecoded(PEMethodScope methodScope) { + if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) { + ensureStateAfterDecoded(methodScope); + + assert methodScope.exceptionPlaceholderNode == null; + methodScope.exceptionPlaceholderNode = methodScope.graph.add(new ExceptionPlaceholderNode()); + registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false); + FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId); + + if (methodScope.caller != null) { + ensureOuterStateDecoded(methodScope.caller); + exceptionState.setOuterFrameState(methodScope.caller.outerState); + } + methodScope.exceptionState = exceptionState; + } + } + @Override protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) { PEMethodScope methodScope = (PEMethodScope) s; if (methodScope.isInlinedMethod()) { if (node instanceof SimpleInfopointNode) { - methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition); + methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition); return node; } else if (node instanceof FrameState) { - FrameState stateAtExceptionEdge = null; - if (methodScope.invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) methodScope.invoke); - ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge(); - stateAtExceptionEdge = obj.stateAfter(); + FrameState frameState = (FrameState) node; + + ensureOuterStateDecoded(methodScope); + if (frameState.bci < 0) { + ensureExceptionStateDecoded(methodScope); } - if (methodScope.outerFrameState == null) { - FrameState stateAtReturn = methodScope.invoke.stateAfter(); - Kind invokeReturnKind = methodScope.invoke.asNode().getKind(); - methodScope.outerFrameState = stateAtReturn.duplicateModifiedDuringCall(methodScope.invoke.bci(), invokeReturnKind); - } - return InliningUtil.processFrameState((FrameState) node, methodScope.invoke, methodScope.method, stateAtExceptionEdge, methodScope.outerFrameState, true); + return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, methodScope.method, methodScope.exceptionState, methodScope.outerState, true); } else if (node instanceof MonitorIdNode) { - InliningUtil.processMonitorId(methodScope.invoke, (MonitorIdNode) node); + ensureOuterStateDecoded(methodScope); + InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node); return node; } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Tue Apr 14 14:01:18 2015 +0200 @@ -42,7 +42,6 @@ import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.*; @@ -50,6 +49,7 @@ import com.oracle.graal.phases.common.inlining.*; import com.oracle.graal.phases.tiers.*; import com.oracle.graal.phases.util.*; +import com.oracle.graal.replacements.*; import com.oracle.graal.truffle.debug.*; import com.oracle.graal.truffle.nodes.*; import com.oracle.graal.truffle.nodes.asserts.*; @@ -164,28 +164,78 @@ private class PEInlineInvokePlugin implements InlineInvokePlugin { - private final boolean duringParsing; private Deque inlining; private OptimizedDirectCallNode lastDirectCallNode; - private final Replacements replacements; + private final ReplacementsImpl replacements; - private final InvocationPlugins invocationPlugins; - private final LoopExplosionPlugin loopExplosionPlugin; - - public PEInlineInvokePlugin(TruffleInlining inlining, Replacements replacements, boolean duringParsing, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin) { + public PEInlineInvokePlugin(TruffleInlining inlining, ReplacementsImpl replacements) { this.inlining = new ArrayDeque<>(); this.inlining.push(inlining); this.replacements = replacements; - this.duringParsing = duringParsing; + } + + @Override + public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) { + InlineInfo inlineInfo = replacements.getInlineInfo(builder, original, arguments, returnType); + if (inlineInfo != null) { + return inlineInfo; + } + + if (original.getAnnotation(TruffleBoundary.class) != null) { + return null; + } + assert !replacements.hasSubstitution(original) : "Replacements must have been processed by now"; + assert !builder.parsingReplacement(); + + if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) { + if (original.equals(callSiteProxyMethod)) { + ValueNode arg1 = arguments[0]; + if (!arg1.isConstant()) { + GraalInternalError.shouldNotReachHere("The direct call node does not resolve to a constant!"); + } + + Object callNode = snippetReflection.asObject(Object.class, (JavaConstant) arg1.asConstant()); + if (callNode instanceof OptimizedDirectCallNode) { + OptimizedDirectCallNode directCallNode = (OptimizedDirectCallNode) callNode; + lastDirectCallNode = directCallNode; + } + } else if (original.equals(callDirectMethod)) { + TruffleInliningDecision decision = getDecision(inlining.peek(), lastDirectCallNode); + lastDirectCallNode = null; + if (decision != null && decision.isInline()) { + inlining.push(decision); + builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption())); + return new InlineInfo(callInlinedMethod, false, false); + } + } + } + + return new InlineInfo(original, false, false); + } + + @Override + public void postInline(ResolvedJavaMethod inlinedTargetMethod) { + if (inlinedTargetMethod.equals(callInlinedMethod)) { + inlining.pop(); + } + } + } + + private class ParsingInlineInvokePlugin implements InlineInvokePlugin { + + private final ReplacementsImpl replacements; + private final InvocationPlugins invocationPlugins; + private final LoopExplosionPlugin loopExplosionPlugin; + private final boolean inlineDuringParsing; + + public ParsingInlineInvokePlugin(ReplacementsImpl replacements, InvocationPlugins invocationPlugins, LoopExplosionPlugin loopExplosionPlugin, boolean inlineDuringParsing) { + this.replacements = replacements; this.invocationPlugins = invocationPlugins; this.loopExplosionPlugin = loopExplosionPlugin; + this.inlineDuringParsing = inlineDuringParsing; } private boolean hasMethodHandleArgument(ValueNode[] arguments) { - /* - * We want to process invokes that have a constant MethodHandle parameter. And the - * method must be statically bound, otherwise we do not have a single target method. - */ for (ValueNode argument : arguments) { if (argument.isConstant()) { JavaConstant constant = argument.asJavaConstant(); @@ -197,58 +247,35 @@ return false; } + @Override public InlineInfo getInlineInfo(GraphBuilderContext builder, ResolvedJavaMethod original, ValueNode[] arguments, JavaType returnType) { - if (duringParsing && (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original))) { + InlineInfo inlineInfo = replacements.getInlineInfo(builder, original, arguments, returnType); + if (inlineInfo != null) { + return inlineInfo; + } + + if (invocationPlugins.lookupInvocation(original) != null || loopExplosionPlugin.shouldExplodeLoops(original)) { return null; } - if (original.getAnnotation(TruffleBoundary.class) != null) { return null; } - if (replacements != null && replacements.hasSubstitution(original)) { + assert !replacements.hasSubstitution(original) : "Replacements must have been processed by now"; + + if (original.equals(callSiteProxyMethod) || original.equals(callDirectMethod)) { return null; } - assert !builder.parsingReplacement(); - if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) { - if (original.equals(callSiteProxyMethod)) { - if (duringParsing) { - return null; - } - ValueNode arg1 = arguments[0]; - if (!arg1.isConstant()) { - GraalInternalError.shouldNotReachHere("The direct call node does not resolve to a constant!"); - } - - Object callNode = snippetReflection.asObject(Object.class, (JavaConstant) arg1.asConstant()); - if (callNode instanceof OptimizedDirectCallNode) { - OptimizedDirectCallNode directCallNode = (OptimizedDirectCallNode) callNode; - lastDirectCallNode = directCallNode; - } - } else if (original.equals(callDirectMethod)) { - if (duringParsing) { - return null; - } - TruffleInliningDecision decision = getDecision(inlining.peek(), lastDirectCallNode); - lastDirectCallNode = null; - if (decision != null && decision.isInline()) { - inlining.push(decision); - builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption())); - return new InlineInfo(callInlinedMethod, false, false); - } - } + if (hasMethodHandleArgument(arguments)) { + /* + * We want to inline invokes that have a constant MethodHandle parameter to remove + * invokedynamic related calls as early as possible. + */ + return new InlineInfo(original, false, false); } - - if (duringParsing && (!original.hasBytecodes() || original.getCode().length >= TrivialInliningSize.getValue() || builder.getDepth() >= InlineDuringParsingMaxDepth.getValue()) && - !hasMethodHandleArgument(arguments)) { - return null; + if (inlineDuringParsing && original.hasBytecodes() && original.getCode().length < TrivialInliningSize.getValue() && builder.getDepth() < InlineDuringParsingMaxDepth.getValue()) { + return new InlineInfo(original, false, false); } - return new InlineInfo(original, false, false); - } - - public void postInline(ResolvedJavaMethod inlinedTargetMethod) { - if (inlinedTargetMethod.equals(callInlinedMethod)) { - inlining.pop(); - } + return null; } } @@ -279,7 +306,7 @@ plugins.setParameterPlugin(new InterceptReceiverPlugin(callTarget)); callTarget.setInlining(new TruffleInlining(callTarget, new DefaultInliningPolicy())); - InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), false, null, null); + InlineInvokePlugin inlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), (ReplacementsImpl) providers.getReplacements()); if (PrintTruffleExpansionHistogram.getValue()) { inlinePlugin = new HistogramInlineInvokePlugin(graph, inlinePlugin); } @@ -303,15 +330,16 @@ newConfig.setUseProfiling(false); Plugins plugins = newConfig.getPlugins(); plugins.setLoadFieldPlugin(new InterceptLoadFieldPlugin()); - plugins.setInlineInvokePlugin(new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), true, parsingInvocationPlugins, loopExplosionPlugin)); + plugins.setInlineInvokePlugin(new ParsingInlineInvokePlugin((ReplacementsImpl) providers.getReplacements(), parsingInvocationPlugins, loopExplosionPlugin, + !PrintTruffleExpansionHistogram.getValue())); CachingPEGraphDecoder decoder = new CachingPEGraphDecoder(providers, newConfig, AllowAssumptions.from(graph.getAssumptions() != null)); ParameterPlugin parameterPlugin = new InterceptReceiverPlugin(callTarget); - InvocationPlugins decodingInvocationPlugins = new InvocationPlugins(providers.getMetaAccess()); + InvocationPlugins decodingInvocationPlugins = new InvocationPlugins(parsingInvocationPlugins.getParent()); TruffleGraphBuilderPlugins.registerInvocationPlugins(providers.getMetaAccess(), decodingInvocationPlugins, false, snippetReflection); - InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), providers.getReplacements(), false, decodingInvocationPlugins, loopExplosionPlugin); + InlineInvokePlugin decodingInlinePlugin = new PEInlineInvokePlugin(callTarget.getInlining(), (ReplacementsImpl) providers.getReplacements()); if (PrintTruffleExpansionHistogram.getValue()) { decodingInlinePlugin = new HistogramInlineInvokePlugin(graph, decodingInlinePlugin); } @@ -431,7 +459,9 @@ if (!TruffleCompilerOptions.TruffleInlineAcrossTruffleBoundary.getValue()) { // Do not inline across Truffle boundaries. for (MethodCallTargetNode mct : graph.getNodes(MethodCallTargetNode.TYPE)) { - mct.invoke().setUseForInlining(false); + if (mct.targetMethod().getAnnotation(TruffleBoundary.class) != null) { + mct.invoke().setUseForInlining(false); + } } } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java Tue Apr 14 14:01:18 2015 +0200 @@ -32,8 +32,8 @@ import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.graph.*; import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.InvocationPlugins.Receiver; import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; +import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.extended.*; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -157,7 +157,6 @@ // Check that the "probed" AST still executes correctly assertEquals(13, callTarget1.call()); - } @Test @@ -178,11 +177,10 @@ // Check instrumentation with the simplest kind of counters. // They should all be removed when the check is finished. - checkCounters(probe, callTarget, rootNode, new TestInstrumentCounter(), new TestInstrumentCounter(), new TestInstrumentCounter()); + checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter()); // Now try with the more complex flavor of listener - checkCounters(probe, callTarget, rootNode, new TestASTInstrumentCounter(), new TestASTInstrumentCounter(), new TestASTInstrumentCounter()); - + checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter()); } private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) { @@ -280,12 +278,10 @@ assertEquals(counterB.leaveCount(), 8); assertEquals(counterC.enterCount(), 2); assertEquals(counterC.leaveCount(), 2); - } @Test public void testTagging() { - // Applies appropriate tags final TestASTProber astProber = new TestASTProber(); Probe.registerASTProber(astProber); @@ -341,7 +337,6 @@ assertEquals(valueCounter.count, 2); Probe.unregisterASTProber(astProber); - } private interface TestCounter { @@ -353,92 +348,107 @@ void attach(Probe probe); void dispose(); - } /** - * A counter for the number of times execution enters and leaves a probed AST node, using the - * simplest kind of listener. + * A counter for the number of times execution enters and leaves a probed AST node. */ - private class TestInstrumentCounter implements TestCounter { + private class TestSimpleInstrumentCounter implements TestCounter { public int enterCount = 0; public int leaveCount = 0; public final Instrument instrument; - public TestInstrumentCounter() { + public TestSimpleInstrumentCounter() { this.instrument = Instrument.create(new SimpleInstrumentListener() { - @Override public void enter(Probe probe) { enterCount++; } - @Override - public void returnAny(Probe probe) { + public void returnVoid(Probe probe) { + leaveCount++; + } + + public void returnValue(Probe probe, Object result) { + leaveCount++; + } + + public void returnExceptional(Probe probe, Exception exception) { leaveCount++; } }, "Instrumentation Test Counter"); - } + @Override public int enterCount() { return enterCount; } + @Override public int leaveCount() { return leaveCount; } + @Override public void attach(Probe probe) { probe.attach(instrument); } + @Override public void dispose() { instrument.dispose(); } } /** - * A counter for the number of times execution enters and leaves a probed AST node, using the - * simplest kind of listener. + * A counter for the number of times execution enters and leaves a probed AST node. */ - private class TestASTInstrumentCounter implements TestCounter { + private class TestStandardInstrumentCounter implements TestCounter { public int enterCount = 0; public int leaveCount = 0; public final Instrument instrument; - public TestASTInstrumentCounter() { - this.instrument = Instrument.create(new SimpleASTInstrumentListener() { + public TestStandardInstrumentCounter() { + this.instrument = Instrument.create(new StandardInstrumentListener() { - @Override public void enter(Probe probe, Node node, VirtualFrame vFrame) { enterCount++; } - @Override - public void returnAny(Probe probe, Node node, VirtualFrame vFrame) { + public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { + leaveCount++; + } + + public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + leaveCount++; + } + + public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { leaveCount++; } }, "Instrumentation Test Counter"); - } + @Override public int enterCount() { return enterCount; } + @Override public int leaveCount() { return leaveCount; } + @Override public void attach(Probe probe) { probe.attach(instrument); } + @Override public void dispose() { instrument.dispose(); } @@ -475,7 +485,7 @@ /** * Counts the number of "enter" events at probed nodes using the simplest AST listener. */ - static final class TestInstrumentListener extends DefaultInstrumentListener { + static final class TestSimpleInstrumentListener extends DefaultSimpleInstrumentListener { public int counter = 0; @@ -483,13 +493,12 @@ public void enter(Probe probe) { counter++; } - } /** * Counts the number of "enter" events at probed nodes using the AST listener. */ - static final class TestASTInstrumentListener extends DefaultASTInstrumentListener { + static final class TestASTInstrumentListener extends DefaultStandardInstrumentListener { public int counter = 0; @@ -497,7 +506,6 @@ public void enter(Probe probe, Node node, VirtualFrame vFrame) { counter++; } - } /** @@ -514,7 +522,7 @@ // where we want to count executions. // it will get copied when ASTs cloned, so // keep the count in this outer class. - probe.attach(Instrument.create(new DefaultInstrumentListener() { + probe.attach(Instrument.create(new DefaultSimpleInstrumentListener() { @Override public void enter(Probe p) { @@ -539,5 +547,4 @@ tagCount++; } } - } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Tue Apr 14 14:01:18 2015 +0200 @@ -86,7 +86,6 @@ public Object execute(VirtualFrame vFrame) { probeNode.enter(child, vFrame); Object result; - try { result = child.execute(vFrame); probeNode.returnValue(child, vFrame, result); @@ -96,7 +95,6 @@ probeNode.returnExceptional(child, vFrame, e); throw (e); } - return result; } } @@ -169,4 +167,17 @@ } } + static class TestSplicedCounterNode extends SplicedNode { + + private long count; + + @Override + public void enter(Node node, VirtualFrame vFrame) { + count++; + } + + public long getCount() { + return count; + } + } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,90 @@ +/* + * 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 com.oracle.truffle.api.test.instrument; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestSplicedCounterNode; + +/** + * Tests the kind of instrumentation where a client can provide an AST fragment to be + * spliced directly into the AST. + */ +public class SpliceInstrumentTest { + + @Test + public void testSpliceInstrumentListener() { + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + + // Ensure it executes correctly + assertEquals(13, callTarget1.call()); + + // Probe the addition node + final Probe probe = addNode.probe(); + + assertEquals(13, callTarget1.call()); + + // Attach a null listener; it never actually attaches a node. + final Instrument instrument = Instrument.create(new SpliceInstrumentListener() { + + public SplicedNode getSpliceNode(Probe p) { + return null; + } + + }, null); + probe.attach(instrument); + + assertEquals(13, callTarget1.call()); + + final TestSplicedCounterNode counter = new TestSplicedCounterNode(); + + // Attach a listener that splices an execution counter into the AST. + probe.attach(Instrument.create(new SpliceInstrumentListener() { + + public SplicedNode getSpliceNode(Probe p) { + return counter; + } + + }, null)); + assertEquals(0, counter.getCount()); + + assertEquals(13, callTarget1.call()); + + assertEquals(1, counter.getCount()); + + } + +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +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 com.oracle.truffle.api.test.instrument; - -import static org.junit.Assert.*; - -import org.junit.*; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; - -/** - * Tests instrumentation where a client can attach a node that gets attached into the AST. - */ -public class ToolNodeInstrumentationTest { - - @Test - public void testToolNodeListener() { - // Create a simple addition AST - final TruffleRuntime runtime = Truffle.getRuntime(); - final TestValueNode leftValueNode = new TestValueNode(6); - final TestValueNode rightValueNode = new TestValueNode(7); - final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); - final TestRootNode rootNode = new TestRootNode(addNode); - final CallTarget callTarget1 = runtime.createCallTarget(rootNode); - - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); - - // Probe the addition node - final Probe probe = addNode.probe(); - - assertEquals(13, callTarget1.call()); - - // Attach a listener that never actually attaches a node. - final Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { - - public ToolNode getToolNode(Probe p) { - return null; - } - - }, null); - probe.attach(instrument); - - assertEquals(13, callTarget1.call()); - - final int[] count = new int[1]; - - // Attach a listener that never actually attaches a node. - probe.attach(Instrument.create(new ToolNodeInstrumentListener() { - - public ToolNode getToolNode(Probe p) { - return new ToolNode() { - - @Override - public void enter(Node node, VirtualFrame vFrame) { - count[0] = count[0] + 1; - } - }; - } - - }, null)); - assertEquals(0, count[0]); - - assertEquals(13, callTarget1.call()); - - assertEquals(1, count[0]); - - } - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Tue Apr 14 14:01:18 2015 +0200 @@ -54,9 +54,9 @@ // Initially has only the default tag assertEquals(source.getSourceTags().size(), 1); - assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL)); - assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL)); - assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source)); + assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL)); + assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL)); + assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source)); assertFalse(source.isTaggedAs(testTag)); assertEquals(Source.findSourcesTaggedAs(testTag).size(), 0); @@ -67,9 +67,9 @@ // Now there are exactly two tags assertEquals(source.getSourceTags().size(), 2); - assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL)); - assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL)); - assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source)); + assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL)); + assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL)); + assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source)); assertTrue(source.getSourceTags().contains(testTag)); assertTrue(source.isTaggedAs(testTag)); @@ -82,9 +82,9 @@ // Nothing has changed assertEquals(source.getSourceTags().size(), 2); - assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL)); - assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL)); - assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source)); + assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL)); + assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL)); + assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source)); assertTrue(source.getSourceTags().contains(testTag)); assertTrue(source.isTaggedAs(testTag)); @@ -135,7 +135,7 @@ assertEquals(newSource[0], source); assertEquals(newTagEvents[0], 1); assertEquals(taggedSource[0], source); - assertEquals(newTag[0], StandardSourceTag.FROM_LITERAL); + assertEquals(newTag[0], Source.Tags.FROM_LITERAL); // reset newSource[0] = null; diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTInstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; - -/** - * A listener of Truffle AST runtime execution events that can collect information, examine the - * execution state at a particular node, and possibly intervene on behalf of an external tool. - */ -public interface ASTInstrumentListener { - - /** - * Receive notification that an AST node's execute method is about to be called. - */ - void enter(Probe probe, Node node, VirtualFrame vFrame); - - /** - * Receive notification that an AST Node's {@code void}-valued execute method has just returned. - */ - void returnVoid(Probe probe, Node node, VirtualFrame vFrame); - - /** - * Receive notification that an AST Node's execute method has just returned a value (boxed if - * primitive). - */ - void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); - - /** - * Receive notification that an AST Node's execute method has just thrown an exception. - */ - void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Apr 14 14:01:18 2015 +0200 @@ -25,145 +25,77 @@ package com.oracle.truffle.api.instrument; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; -import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; -// TODO (mlvdv) migrate some of this to external documentation. -// TODO (mlvdv) move all this to a factory implemented in .impl (together with Probe), -// then break out some of the nested classes into package privates. +// TODO (mlvdv) these statics should not be global. Move them to some kind of context. +// TODO (mlvdv) migrate factory into .impl (together with Probe)? break out nested classes? + /** - * A dynamically added/removed binding between a {@link Probe}, which provides notification of - * execution events taking place at a {@link Node} in a Guest Language (GL) Truffle AST, - * and a listener, which consumes notifications on behalf of an external tool. There are at - * present two kinds of listeners that be used: - *
    - *
  1. {@link InstrumentListener} is the simplest and is intended for tools that require no access - * to the internal execution state of the Truffle execution, only that execution has passed - * through a particular location in the program. Information about that location is made available - * via the {@link Probe} argument in notification methods, including the {@linkplain SourceSection - * source location} of the node and any {@linkplain SyntaxTag tags} that have been applied to the - * node.
  2. - *
  3. {@link ASTInstrumentListener} reports the same events and {@link Probe} argument, but - * additionally provides access to the execution state via the explicit {@link Node} and - * {@link Frame} at the current execution site.
  4. - *
- *

- *

Summary: How to "instrument" an AST location:

- *

+ * A binding between: *

    - *
  1. Create an implementation of a listener interface.
  2. - *
  3. Create an Instrument via factory methods - * {@link Instrument#create(InstrumentListener, String)} or - * {@link Instrument#create(ASTInstrumentListener, String)}.
  4. - *
  5. "Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event - * notifications begin to arrive at the listener.
  6. - *
  7. When no longer needed, "detach" the Instrument via {@link ASTInstrument#dispose()}, at which - * point event notifications to the listener cease, and the Instrument becomes unusable.
  8. - *
- *

- *

Options for creating listeners:

- *

- *

    - *
  1. Implement one of the listener interfaces: {@link InstrumentListener} or - * {@link ASTInstrumentListener} . Their event handling methods account for both the entry into an - * AST node (about to call) and three possible kinds of execution return from an AST node.
  2. - *
  3. Extend one of the helper implementations: {@link DefaultInstrumentListener} or - * {@link DefaultASTInstrumentListener}. These provide no-op implementation of every listener - * method, so only the methods of interest need to be overridden.
  4. - *
  5. Extend one of the helper implementations: {@link SimpleInstrumentListener} or - * {@link SimpleASTInstrumentListener}. These re-route all execution returns to a single - * method, ignoring return values, so only two methods (for "enter" and "return") will notify all - * events.
  6. + *
  7. A {@link Probe}: a source of execution events taking place at a program location in + * an executing Truffle AST, and
  8. + *
  9. A listener: a consumer of execution events on behalf of an external client. *
*

- *

General guidelines for {@link ASTInstrumentListener} implementation:

+ * Client-oriented documentation for the use of Instruments is available online at https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events *

- * Unlike the listener interface {@link InstrumentListener}, which isolates implementations - * completely from Truffle internals (and is thus Truffle-safe), implementations of - * {@link ASTInstrumentListener} can interact directly with (and potentially affect) Truffle - * execution in general and Truffle optimization in particular. For example, it is possible to - * implement a debugger with this interface. - *

+ * The implementation of Instruments is complicated by the requirement that Truffle be able to clone + * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to + * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes. *

- * As a consequence, implementations of {@link ASTInstrumentListener} effectively become part of the - * Truffle execution and must be coded according to general guidelines for Truffle implementations. - * For example: - *

    - *
  • Do not store {@link Frame} or {@link Node} references in fields.
  • - *
  • Prefer {@code final} fields and (where performance is important) short methods.
  • - *
  • If needed, pass along the {@link VirtualFrame} reference from an event notification as far as - * possible through code that is expected to be inlined, since this incurs no runtime overhead. When - * access to frame data is needed, substitute a more expensive {@linkplain Frame#materialize() - * materialized} representation of the frame.
  • - *
  • If a listener calls back to its tool during event handling, and if performance is an issue, - * then this should be through a final "callback" field in the instrument, and the called methods - * should be minimal.
  • - *
  • On the other hand, implementations should prevent Truffle from inlining beyond a reasonable - * point with the method annotation {@link TruffleBoundary}.
  • - *
  • The implicit "outer" pointer in a non-static inner class is a useful (and - * Truffle-optimizable) way to implement callbacks to owner tools.
  • - *
  • Primitive-valued return events are boxed for event notification, but Truffle will eliminate - * the boxing if they are cast back to their primitive values quickly (in particular before crossing - * any {@link TruffleBoundary} annotations). - *
+ * AST cloning is intended to be as transparent as possible to clients. This is encouraged + * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than + * the properties associated with a Probe: it's {@link SourceSection} and any associated instances + * of {@link SyntaxTag}. *

- *

Allowing for AST cloning:

+ * AST cloning is not transparent to clients that use the + * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance + * (and thus the AST instance) where the event takes place. *

- * Truffle routinely clones ASTs, which has consequences for implementations of - * {@link ASTInstrumentListener} (but not for implementations of {@link InstrumentListener}, from - * which cloning is hidden). - *

    - *
  • Even though a {@link Probe} is uniquely associated with a particular location in the - * executing Guest Language program, execution events at that location will in general be - * implemented by different {@link Node} instances, i.e. clones of the originally probed - * node.
  • - *
  • Because of cloning the {@link Node} supplied with notifications to a particular - * listener will vary, but because they all represent the same GL program location the events should - * be treated as equivalent for most purposes.
  • - *
- *

- *

Access to execution state via {@link ASTInstrumentListener}:

+ *

Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}

*

*

    - *
  • Notification arguments provide primary access to the GL program's execution states: - *
      - *
    • {@link Node}: the concrete node (in one of the AST's clones) from which the event originated. + *
    • A new Instrument is created in permanent association with a client-provided + * listener.
    • + * + *
    • Multiple Instruments may share a single listener.
    • + * + *
    • An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a + * Probe, at which time the Instrument begins routing execution events from the Probe's AST location + * to the Instrument's listener.
    • + * + *
    • Neither Instruments nor Probes are {@link Node}s.
    • + * + *
    • A Probe has a single source-based location in an AST, but manages a separate + * instrumentation chain of Nodes at the equivalent location in each clone of the AST.
    • + *
    • When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned + * along with the rest of the AST.
    • + * + *
    • When a new Instrument (for example an instance of {@link SimpleInstrument} is attached to a + * Probe, the Instrument inserts a new instance of its private Node type, + * {@link SimpleInstrument.SimpleInstrumentNode}, into each of the instrument chains + * managed by the Probe, i.e. one node instance per existing clone of the AST.
    • + * + *
    • If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the + * Instrument's private Node type will be cloned along with the rest of the the AST.
    • + *
    • Each Instrument's private Node type is a dynamic inner class whose only state is in the + * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener. *
    • - *
    • {@link VirtualFrame}: the current execution frame. - *
    - *
  • Truffle global information is available, for example the execution - * {@linkplain TruffleRuntime#iterateFrames(FrameInstanceVisitor) stack}.
  • - *
  • Additional API access to execution state may be added in the future.
  • + * + *
  • When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the + * Instrument searches every instrument chain associated with the Probe and removes the instance of + * its private Node type.
  • + * + *
  • Attaching and disposing an Instrument at a Probe deoptimizes any compilations of the + * AST.
  • + * *
- *

- *

Activating and deactivating Instruments:

- *

- * Instruments are single-use: - *

    - *
  • An instrument becomes active only when attached to a Probe via - * {@link Probe#attach(Instrument)}, and it may only be attached to a single Probe. It is a runtime - * error to attempt attaching a previously attached instrument.
  • - *
  • Attaching an instrument modifies every existing clone of the AST to which it is being - * attached, which can trigger deoptimization.
  • - *
  • The method {@link Instrument#dispose()} makes an instrument inactive by removing it from the - * Probe to which it was attached and rendering it permanently inert.
  • - *
  • Disposal removes the implementation of an instrument from all ASTs to which it was attached, - * which can trigger deoptimization.
  • - *
- *

- *

Sharing listeners:

- *

- * Although an Instrument may only be attached to a single Probe, a listener can be shared among - * multiple Instruments. This can be useful for observing events that might happen at different - * locations in a single AST, for example all assignments to a particular variable. In this case a - * new Instrument would be created and attached at each assignment node, but all the Instruments - * would be created with the same listener. - *

- * Disclaimer: experimental; under development. * * @see Probe * @see TruffleEvents @@ -177,34 +109,35 @@ * @param instrumentInfo optional description of the instrument's role, useful for debugging. * @return a new instrument, ready for attachment at a probe */ - public static Instrument create(InstrumentListener listener, String instrumentInfo) { - return new BasicInstrument(listener, instrumentInfo); + public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) { + return new SimpleInstrument(listener, instrumentInfo); } /** * Creates an instrument that will route execution events to a listener, along with access to * internal execution state. * - * @param astListener a listener for event generated by the instrument that provides access to - * internal execution state + * @param standardListener a listener for event generated by the instrument that provides access + * to internal execution state * @param instrumentInfo optional description of the instrument's role, useful for debugging. * @return a new instrument, ready for attachment at a probe */ - public static Instrument create(ASTInstrumentListener astListener, String instrumentInfo) { - return new ASTInstrument(astListener, instrumentInfo); + public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) { + return new StandardInstrument(standardListener, instrumentInfo); } /** * Creates an instrument that, when executed the first time in any particular AST location, - * invites the tool to provide an AST fragment for attachment/adoption into the running AST. + * invites the tool to provide an AST fragment for splicing directly into the running + * AST. * - * @param toolNodeListener a listener for the tool that can request an AST fragment + * @param spliceListener a callback to the client that requests an AST node to be splice. * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful * for debugging. * @return a new instrument, ready for attachment at a probe. */ - public static Instrument create(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { - return new ToolNodeInstrument(toolNodeListener, instrumentInfo); + public static Instrument create(SpliceInstrumentListener spliceListener, String instrumentInfo) { + return new SpliceInstrument(spliceListener, instrumentInfo); } // TODO (mlvdv) experimental @@ -232,8 +165,7 @@ } /** - * Removes this instrument (and any clones) from the probe to which it attached and renders the - * instrument inert. + * Removes this instrument from the probe to which it attached and renders the instrument inert. * * @throws IllegalStateException if this instrument has already been disposed */ @@ -266,23 +198,28 @@ abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode); /** - * An instrument that propagates events to an instance of {@link InstrumentListener}. + * Removes this instrument from an instrument chain. */ - private static final class BasicInstrument extends Instrument { + abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode); + + /** + * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}. + */ + private static final class SimpleInstrument extends Instrument { /** * Tool-supplied listener for events. */ - private final InstrumentListener instrumentListener; + private final SimpleInstrumentListener simpleListener; - private BasicInstrument(InstrumentListener basicListener, String instrumentInfo) { + private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) { super(instrumentInfo); - this.instrumentListener = basicListener; + this.simpleListener = simpleListener; } @Override AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new BasicInstrumentNode(nextNode); + return new SimpleInstrumentNode(nextNode); } @Override @@ -294,7 +231,7 @@ return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. - found = instrumentNode.removeFromChain(BasicInstrument.this); + found = instrumentNode.removeFromChain(SimpleInstrument.this); } if (!found) { throw new IllegalStateException("Couldn't find instrument node to remove: " + this); @@ -303,35 +240,35 @@ } @NodeInfo(cost = NodeCost.NONE) - private final class BasicInstrumentNode extends AbstractInstrumentNode { + private final class SimpleInstrumentNode extends AbstractInstrumentNode { - private BasicInstrumentNode(AbstractInstrumentNode nextNode) { + private SimpleInstrumentNode(AbstractInstrumentNode nextNode) { super(nextNode); } public void enter(Node node, VirtualFrame vFrame) { - BasicInstrument.this.instrumentListener.enter(BasicInstrument.this.probe); + SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe); if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { - BasicInstrument.this.instrumentListener.returnVoid(BasicInstrument.this.probe); + SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe); if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - BasicInstrument.this.instrumentListener.returnValue(BasicInstrument.this.probe, result); + SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result); if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - BasicInstrument.this.instrumentListener.returnExceptional(BasicInstrument.this.probe, exception); + SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception); if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } @@ -339,34 +276,29 @@ public String instrumentationInfo() { final String info = getInstrumentInfo(); - return info != null ? info : instrumentListener.getClass().getSimpleName(); + return info != null ? info : simpleListener.getClass().getSimpleName(); } } } /** - * Removes this instrument from an instrument chain. + * An instrument that propagates events to an instance of {@link StandardInstrumentListener}. */ - abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode); - - /** - * An instrument that propagates events to an instance of {@link ASTInstrumentListener}. - */ - private static final class ASTInstrument extends Instrument { + private static final class StandardInstrument extends Instrument { /** * Tool-supplied listener for AST events. */ - private final ASTInstrumentListener astListener; + private final StandardInstrumentListener standardListener; - private ASTInstrument(ASTInstrumentListener astListener, String instrumentInfo) { + private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) { super(instrumentInfo); - this.astListener = astListener; + this.standardListener = standardListener; } @Override AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new ASTInstrumentNode(nextNode); + return new StandardInstrumentNode(nextNode); } @Override @@ -378,7 +310,7 @@ return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. - found = instrumentNode.removeFromChain(ASTInstrument.this); + found = instrumentNode.removeFromChain(StandardInstrument.this); } if (!found) { throw new IllegalStateException("Couldn't find instrument node to remove: " + this); @@ -387,35 +319,35 @@ } @NodeInfo(cost = NodeCost.NONE) - private final class ASTInstrumentNode extends AbstractInstrumentNode { + private final class StandardInstrumentNode extends AbstractInstrumentNode { - private ASTInstrumentNode(AbstractInstrumentNode nextNode) { + private StandardInstrumentNode(AbstractInstrumentNode nextNode) { super(nextNode); } public void enter(Node node, VirtualFrame vFrame) { - ASTInstrument.this.astListener.enter(ASTInstrument.this.probe, node, vFrame); + standardListener.enter(StandardInstrument.this.probe, node, vFrame); if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { - ASTInstrument.this.astListener.returnVoid(ASTInstrument.this.probe, node, vFrame); + standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame); if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - ASTInstrument.this.astListener.returnValue(ASTInstrument.this.probe, node, vFrame, result); + standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result); if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - ASTInstrument.this.astListener.returnExceptional(ASTInstrument.this.probe, node, vFrame, exception); + standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception); if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } @@ -423,30 +355,31 @@ public String instrumentationInfo() { final String info = getInstrumentInfo(); - return info != null ? info : astListener.getClass().getSimpleName(); + return info != null ? info : standardListener.getClass().getSimpleName(); } } - } + // TODO (mlvdv) EXPERIMENTAL- UNDER DEVELOPMENT /** - * An instrument that propagates events to an instance of {@link ASTInstrumentListener}. + * An instrument that allows clients to "splice" an AST fragment directly into a Probe's + * instrumentation chain, and thus directly into the executing Truffle AST. */ - private static final class ToolNodeInstrument extends Instrument { + private static final class SpliceInstrument extends Instrument { /** * Tool-supplied listener for AST events. */ - private final ToolNodeInstrumentListener toolNodeListener; + private final SpliceInstrumentListener spliceListener; - private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { + private SpliceInstrument(SpliceInstrumentListener spliceListener, String instrumentInfo) { super(instrumentInfo); - this.toolNodeListener = toolNodeListener; + this.spliceListener = spliceListener; } @Override AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new ToolInstrumentNode(nextNode); + return new SpliceInstrumentNode(nextNode); } @Override @@ -458,7 +391,7 @@ return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. - found = instrumentNode.removeFromChain(ToolNodeInstrument.this); + found = instrumentNode.removeFromChain(SpliceInstrument.this); } if (!found) { throw new IllegalStateException("Couldn't find instrument node to remove: " + this); @@ -467,25 +400,25 @@ } @NodeInfo(cost = NodeCost.NONE) - private final class ToolInstrumentNode extends AbstractInstrumentNode { + private final class SpliceInstrumentNode extends AbstractInstrumentNode { - @Child ToolNode toolNode; + @Child SplicedNode splicedNode; - private ToolInstrumentNode(AbstractInstrumentNode nextNode) { + private SpliceInstrumentNode(AbstractInstrumentNode nextNode) { super(nextNode); } public void enter(Node node, VirtualFrame vFrame) { - if (toolNode == null) { - final ToolNode newToolNode = ToolNodeInstrument.this.toolNodeListener.getToolNode(ToolNodeInstrument.this.probe); - if (newToolNode != null) { - toolNode = newToolNode; + if (splicedNode == null) { + final SplicedNode newSplicedNode = SpliceInstrument.this.spliceListener.getSpliceNode(SpliceInstrument.this.probe); + if (newSplicedNode != null) { + splicedNode = newSplicedNode; adoptChildren(); - ToolNodeInstrument.this.probe.invalidateProbeUnchanged(); + SpliceInstrument.this.probe.invalidateProbeUnchanged(); } } - if (toolNode != null) { - toolNode.enter(node, vFrame); + if (splicedNode != null) { + splicedNode.enter(node, vFrame); } if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); @@ -493,8 +426,8 @@ } public void returnVoid(Node node, VirtualFrame vFrame) { - if (toolNode != null) { - toolNode.returnVoid(node, vFrame); + if (splicedNode != null) { + splicedNode.returnVoid(node, vFrame); } if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); @@ -502,8 +435,8 @@ } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (toolNode != null) { - toolNode.returnValue(node, vFrame, result); + if (splicedNode != null) { + splicedNode.returnValue(node, vFrame, result); } if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); @@ -511,8 +444,8 @@ } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (toolNode != null) { - toolNode.returnExceptional(node, vFrame, exception); + if (splicedNode != null) { + splicedNode.returnExceptional(node, vFrame, exception); } if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); @@ -521,10 +454,9 @@ public String instrumentationInfo() { final String info = getInstrumentInfo(); - return info != null ? info : toolNodeListener.getClass().getSimpleName(); + return info != null ? info : spliceListener.getClass().getSimpleName(); } } - } public interface TruffleOptListener { @@ -605,7 +537,6 @@ return info != null ? info : toolOptListener.getClass().getSimpleName(); } } - } @NodeInfo(cost = NodeCost.NONE) @@ -658,7 +589,5 @@ protected String getInstrumentInfo() { return Instrument.this.instrumentInfo; } - } - } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument; - -/** - * A listener of Truffle execution events that can collect information on behalf of an external - * tool. Contextual information about the source of the event, if not stored in the implementation - * of the listener, can be obtained via access to the {@link Probe} that generates the event. - */ -public interface InstrumentListener { - - /** - * Receive notification that an AST node's execute method is about to be called. - */ - void enter(Probe probe); - - /** - * Receive notification that an AST Node's {@code void}-valued execute method has just returned. - */ - void returnVoid(Probe probe); - - /** - * Receive notification that an AST Node's execute method has just returned a value (boxed if - * primitive). - */ - void returnValue(Probe probe, Object result); - - /** - * Receive notification that an AST Node's execute method has just thrown an exception. - */ - void returnExceptional(Probe probe, Exception exception); -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -28,8 +28,9 @@ import com.oracle.truffle.api.nodes.*; /** - * A marker interface for Truffle {@linkplain Node nodes} that support Instrumentation and - * are should not be part of any Guest Language execution semantics. + * A marker interface for Truffle {@linkplain Node nodes} that internally implement the + * Instrumentation Framework. Such nodes should not be part of any Guest Language execution + * semantics, and should in general not be visible to ordinary Instrumentation clients. */ public interface InstrumentationNode { @@ -39,7 +40,8 @@ String instrumentationInfo(); /** - * Events at a Truffle node that get propagated through the Instrumentation Framework. + * Events that propagate through the {@linkplain InstrumentationNode implementation nodes} of + * the Instrumentation Framework, not visible in this form to Instrumentation clients. */ interface TruffleEvents { @@ -62,6 +64,5 @@ * An AST Node's execute method has just thrown an exception. */ void returnExceptional(Node node, VirtualFrame vFrame, Exception exception); - } } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Tue Apr 14 14:01:18 2015 +0200 @@ -34,38 +34,62 @@ import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.utilities.*; -//TODO (mlvdv) migrate some of this to external documentation. +//TODO (mlvdv) these statics should not be global. Move them to some kind of context. + /** - * A binding between a particular location in the Truffle AST representation of a running - * Guest Language (GL) program (i.e. a {@link Node}) and a dynamically managed collection of - * "attached" {@linkplain Instrument instrumentation} for use by external tools. The instrumentation - * is intended to persist at the location, even if the specific node instance is - * {@linkplain Node#replace(Node) replaced}. + * A binding between: + *

    + *
  1. A program location in an executing Truffle AST (defined by a {@link SourceSection}), and
  2. + *
  3. A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that + * receive event notifications on behalf of external clients.
  4. + *
*

- * The effect of a binding is to intercept {@linkplain TruffleEvents execution events} arriving at - * the node and notify each attached {@link Instrument} before execution is allowed to proceed to - * the child. + * Client-oriented documentation for the use of Probes is available online at https://wiki.openjdk.java.net/display/Graal/Finding+Probes *

- * A Probe is "inserted" into a GL node via a call to {@link Node#probe()}. No more than one Probe - * can be inserted at a node. + *

Implementation notes:

*

- * The "probing" of a Truffle AST must be done after it is complete (i.e. with parent pointers - * correctly assigned), but before any executions. This is done by creating an instance of - * {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it - * will be automatically applied to newly created ASTs. - *

- * Each Probe may also have assigned to it any number of {@link SyntaxTag}s, for example identifying - * a node as a {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. Tags can be queried by tools to - * configure behavior relevant to each probed node. - *

- * Instrumentation is implemented by modifying ASTs, both by inserting nodes into each AST at probed - * locations and by attaching additional nodes that implement dynamically attached instruments. - * Attached instrumentation code become, in effect, part of the GL program, and is subject to the - * same levels of optimization as other GL code. This implementation accounts properly for the fact - * that Truffle frequently clones ASTs, along with any attached instrumentation nodes. A - * {@link Probe}, along with attached {@link Instrument}s, represents a logical binding - * with a source code location, producing event notifications that are (mostly) independent of which - * AST clone is executing. + *

    + *
  • A Probe must be permanently associated with a program location, defined by a + * particular {@link SourceSection}, even though: + *
      + *
    • that location is represented in an AST as a {@link Node}, which might be replaced through + * optimizations such as specialization, and
    • + *
    • Truffle may clone the AST so that the location is actually represented by multiple + * Nodes in multiple ASTs.
    • + *
    + *
  • + * + *
  • The effect of the binding is to intercept {@linkplain TruffleEvents execution events} + * arriving at the "probed" AST node and notify each attached {@link Instrument} before execution is + * allowed to proceed to the child and again after execution completes.
  • + * + *
  • A Probe is "inserted" into a GL node via a call to {@link Node#probe()}. No more than one + * Probe can be inserted at a node; a redundant call returns the existing Probe<./li> + * + *
  • The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers + * correctly assigned), but before any cloning or executions. This is done by creating an instance + * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it + * will be applied automatically to every newly created AST.
  • + * + *
  • An AST node becomes probed by insertion of a {@link ProbeNode.WrapperNode} into the + * AST, together with an associated {@link ProbeNode} that routes events to all the + * {@linkplain Instrument Instruments} attached to its instrument chain.
  • + * + *
  • When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well, + * together with their attached instrument chains. The {@link Probe} instance intercepts cloning + * events and keeps track of all copies.
  • + * + *
  • All attached {@link InstrumentationNode}s effectively become part of the running program: + *
      + *
    • Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.
    • + *
    • Bad News: instrumentation code must be implemented carefully to avoid interfering with any + * Truffle optimizations.
    • + *
    + *
  • + * + *
* * @see Instrument * @see ASTProber @@ -173,6 +197,7 @@ return taggedProbes; } + // TODO (mlvdv) can this be generalized to permit multiple traps without a performance hit? /** * Sets the current "tag trap"; there can be no more than one set at a time. *
    diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,74 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.source.*; + +/** + * A receiver of Truffle execution events that can act on behalf of an external client. + *

    + * The {@link Probe} instance provides access to the {@link SourceSection} associated with the + * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. + *

    + * This is the simplest kind of listener, suitable for clients that need no other information about + * the program's execution state at the time of the event. Clients that require access to the AST + * execution state should use {@link StandardInstrumentListener}. + *

    + * Clients are free, of course, to record additional information in the listener implementation that + * carries additional information about the context and reason for the particular {@link Instrument} + * that is to be created from the listener. + */ +public interface SimpleInstrumentListener { + + /** + * Receive notification that a program location is about to be executed. + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void enter(Probe probe); + + /** + * Receive notification that a program location's {@code void}-valued execution has just + * completed. + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void returnVoid(Probe probe); + + /** + * Receive notification that a program location's execution has just completed and returned a + * value (boxed if primitive). + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void returnValue(Probe probe, Object result); + + /** + * Receive notification that a program location's execution has just thrown an exception. + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void returnExceptional(Probe probe, Exception exception); +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SpliceInstrumentListener.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,58 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.truffle.api.instrument; + +/** + * Instrument listener for a tool that works by providing an AST to be spliced directly + * into the AST. + */ +public interface SpliceInstrumentListener { + + /** + * Receive notification that a probed AST node to which the {@link Instrument} is attached is + * about to be executed for the first time. This is a lazy opportunity for the tool to + * optionally add the root of a newly created AST fragment that will be spliced + * directly into the executing AST. The new AST fragment will immediately begin receiving + * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event. + *

    + * AST fragments must be written to Truffle conventions. Some of these conventions are + * especially important if the fragment is to be fully optimized along with it's new parent AST. + *

    + * If this method returns {@code null} then it will be called again the next time the probed + * node is about to be executed. + *

    + * In some situations, this method will be called more than once for a particular Probe, and a + * new instance must be supplied each time. Each instance will be attached at the equivalent + * location in clones of the AST, and so should be behave as if equivalent for most purposes. + *

    + * In some situations the AST fragment supplied by this method maybe cloned for attachment to + * equivalent locations in cloned AST, so care should be taken about any state local to each + * instance of the AST fragment. + * + * @see Instrument + */ + SplicedNode getSpliceNode(Probe probe); + +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SplicedNode.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,55 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Root of a client-provided AST fragment that can be spliced directly into an executing AST via + * {@link Instrument#create(SpliceInstrumentListener, String)}. + *

    + * Note: Instances of this class will in some situations be cloned by the + * instrumentation platform for attachment at equivalent locations in cloned ASTs. + */ +public abstract class SplicedNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode { + + public void enter(Node node, VirtualFrame vFrame) { + } + + public void returnVoid(Node node, VirtualFrame vFrame) { + } + + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + } + + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + } + + public String instrumentationInfo() { + return null; + } + +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,75 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; + +/** + * A receiver of Truffle execution events that can act on behalf of an external client. + *

    + * The {@link Probe} argument provides access to the {@link SourceSection} associated with the + * event, as well as any {@link SyntaxTag}s that have been applied at that AST node. + *

    + * This listener is designed for clients that also require access to the AST execution state at the + * time of the event. Clients that do not require access to the AST execution state should use the + * {@link SimpleInstrumentListener}. + *

    + * Clients are free, of course, to record additional information in the listener implementation that + * carries additional information about the context and reason for the particular {@link Instrument} + * that is to be created from the listener. + */ +public interface StandardInstrumentListener { + + /** + * Receive notification that an AST node's execute method is about to be called. + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void enter(Probe probe, Node node, VirtualFrame vFrame); + + /** + * Receive notification that an AST Node's {@code void}-valued execute method has just returned. + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void returnVoid(Probe probe, Node node, VirtualFrame vFrame); + + /** + * Receive notification that an AST Node's execute method has just returned a value (boxed if + * primitive). + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); + + /** + * Receive notification that an AST Node's execute method has just thrown an exception. + *

    + * Synchronous: Truffle execution waits until the call returns. + */ + void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; - -/** - * Root of a tool-provided AST fragment that can be attached directly into an executing AST via - * {@link Instrument#create(ToolNodeInstrumentListener, String)}. - *

    - * Note: Instances of this class will in some situations be cloned by the - * instrumentation platform for attachment at equivalent locations in cloned parent ASTs. - */ -public abstract class ToolNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode { - - public void enter(Node node, VirtualFrame vFrame) { - } - - public void returnVoid(Node node, VirtualFrame vFrame) { - } - - public void returnValue(Node node, VirtualFrame vFrame, Object result) { - } - - public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - } - - public String instrumentationInfo() { - return null; - } - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument; - -/** - * Instrument listener for a tool that works by providing an AST to be attached/adopted directly - * into the AST. - */ -public interface ToolNodeInstrumentListener { - - /** - * Receive notification that a probed AST node to which the {@link Instrument} is attached is - * about to be executed for the first time. This is a lazy opportunity for the tool to - * optionally add the root of a newly created AST fragment that will be attached/adopted - * directly into the executing AST. The new AST fragment will immediately begin receiving - * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event. - *

    - * AST fragments must be written to Truffle conventions. Some of these conventions are - * especially important if the fragment is to be fully optimized along with it's new parent AST. - *

    - * If this method returns {@code null} then it will be called again the next time the probed - * node is about to be executed. - *

    - * In some situations, this method will be called more than once for a particular Probe, and a - * new instance must be supplied each time. Each instance will be attached at the equivalent - * location in clones of the AST, and so should be behave as if equivalent for most purposes. - *

    - * In some situations the AST fragment supplied by this method maybe cloned for attachment to - * equivalent locations in cloned AST, so care should be taken about any state local to each - * instance of the AST fragment. - * - * @see Instrument - */ - ToolNode getToolNode(Probe probe); - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTInstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument.impl; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; - -/** - * A listener for AST {@linkplain ASTInstrumentListener execution events} that provides a no-op - * implementation of every event. - */ -public class DefaultASTInstrumentListener implements ASTInstrumentListener { - - public void enter(Probe probe, Node node, VirtualFrame vFrame) { - } - - public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { - } - - public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { - } - - public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { - } - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultInstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument.impl; - -import com.oracle.truffle.api.instrument.*; - -/** - * A listener for Truffle execution events that provides a no-op implementation of every event. - */ -public class DefaultInstrumentListener implements InstrumentListener { - - public void enter(Probe probe) { - } - - public void returnVoid(Probe probe) { - } - - public void returnValue(Probe probe, Object result) { - } - - public void returnExceptional(Probe probe, Exception exception) { - } -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,45 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.truffle.api.instrument.impl; + +import com.oracle.truffle.api.instrument.*; + +/** + * A listener for Truffle execution events that provides a no-op implementation of every event. + */ +public class DefaultSimpleInstrumentListener implements SimpleInstrumentListener { + + public void enter(Probe probe) { + } + + public void returnVoid(Probe probe) { + } + + public void returnValue(Probe probe, Object result) { + } + + public void returnExceptional(Probe probe, Exception exception) { + } +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java Tue Apr 14 14:01:18 2015 +0200 @@ -0,0 +1,49 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.truffle.api.instrument.impl; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; + +/** + * A listener for AST {@linkplain StandardInstrumentListener execution events} that provides a no-op + * implementation of every event. + */ +public class DefaultStandardInstrumentListener implements StandardInstrumentListener { + + public void enter(Probe probe, Node node, VirtualFrame vFrame) { + } + + public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { + } + + public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + } + + public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { + } + +} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleASTInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleASTInstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument.impl; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; - -/** - * An abstract listener for AST {@linkplain ASTInstrumentListener execution events} that ignores - * return values and supports handling all events by overriding only two methods: - *

      - *
    • {@link #enter(Probe, Node, VirtualFrame)}, and
    • - *
    • {@link #returnAny(Probe, Node, VirtualFrame)}.
    • - *
    - */ -public abstract class SimpleASTInstrumentListener implements ASTInstrumentListener { - - public void enter(Probe probe, Node node, VirtualFrame vFrame) { - } - - /** - * Receive notification that one of an AST Node's execute methods has just returned by any - * means: with or without a return value (ignored) or via exception (ignored). - * - * @param probe where the event originated - * @param node specific node of the event - * @param vFrame - */ - protected void returnAny(Probe probe, Node node, VirtualFrame vFrame) { - } - - public final void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { - returnAny(probe, node, vFrame); - } - - public final void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { - returnAny(probe, node, vFrame); - } - - public final void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception e) { - returnAny(probe, node, vFrame); - } - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleInstrumentListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleInstrumentListener.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2014, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.instrument.impl; - -import com.oracle.truffle.api.instrument.*; - -/** - * An abstract listener for Truffle {@linkplain InstrumentListener execution events} that ignores - * return values and supports handling all events by overriding only two methods: - *
      - *
    • {@link #enter(Probe)}, and
    • - *
    • {@link #returnAny(Probe)}.
    • - *
    - */ -public abstract class SimpleInstrumentListener implements InstrumentListener { - - public void enter(Probe probe) { - } - - /** - * Receive notification that an execute method has just returned by any means: with or without a - * return value (ignored) or via exception (ignored). - * - * @param probe - */ - protected void returnAny(Probe probe) { - } - - public final void returnVoid(Probe probe) { - returnAny(probe); - } - - public final void returnValue(Probe probe, Object result) { - returnAny(probe); - } - - public final void returnExceptional(Probe probe, Exception e) { - returnAny(probe); - } - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Tue Apr 14 14:01:18 2015 +0200 @@ -78,6 +78,51 @@ // TODO (mlvdv) consider canonicalizing and reusing SourceSection instances // TOOD (mlvdv) connect SourceSections into a spatial tree for fast geometric lookup + public enum Tags implements SourceTag { + + /** + * From bytes. + */ + FROM_BYTES("bytes", "read from bytes"), + + /** + * Read from a file. + */ + FROM_FILE("file", "read from a file"), + + /** + * From literal text. + */ + FROM_LITERAL("literal", "from literal text"), + + /** + * From a {@linkplain java.io.Reader Reader}. + */ + FROM_READER("reader", "read from a Java Reader"), + + /** + * Read from a URL. + */ + FROM_URL("URL", "read from a URL"); + + private final String name; + private final String description; + + private Tags(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + } + /** * All Sources that have been created. */ @@ -119,7 +164,7 @@ if (reset) { source.reset(); } - notifyNewSource(source).tagAs(StandardSourceTag.FROM_FILE); + notifyNewSource(source).tagAs(Tags.FROM_FILE); return source; } @@ -161,7 +206,7 @@ filePathToSource.put(path, new WeakReference<>(source)); } } - notifyNewSource(source).tagAs(StandardSourceTag.FROM_FILE); + notifyNewSource(source).tagAs(Tags.FROM_FILE); return source; } @@ -176,7 +221,7 @@ public static Source fromText(CharSequence chars, String description) { assert chars != null; final LiteralSource source = new LiteralSource(description, chars.toString()); - notifyNewSource(source).tagAs(StandardSourceTag.FROM_LITERAL); + notifyNewSource(source).tagAs(Tags.FROM_LITERAL); return source; } @@ -190,7 +235,7 @@ */ public static Source fromURL(URL url, String description) throws IOException { final URLSource source = URLSource.get(url, description); - notifyNewSource(source).tagAs(StandardSourceTag.FROM_URL); + notifyNewSource(source).tagAs(Tags.FROM_URL); return source; } @@ -204,7 +249,7 @@ */ public static Source fromReader(Reader reader, String description) throws IOException { final LiteralSource source = new LiteralSource(description, read(reader)); - notifyNewSource(source).tagAs(StandardSourceTag.FROM_READER); + notifyNewSource(source).tagAs(Tags.FROM_READER); return source; } @@ -237,7 +282,7 @@ */ public static Source fromBytes(byte[] bytes, int byteIndex, int length, String description, BytesDecoder decoder) { final BytesSource source = new BytesSource(description, bytes, byteIndex, length, decoder); - notifyNewSource(source).tagAs(StandardSourceTag.FROM_BYTES); + notifyNewSource(source).tagAs(Tags.FROM_BYTES); return source; } @@ -252,7 +297,7 @@ public static Source asPseudoFile(CharSequence chars, String pseudoFileName) { final Source source = new LiteralSource(pseudoFileName, chars.toString()); filePathToSource.put(pseudoFileName, new WeakReference<>(source)); - notifyNewSource(source).tagAs(StandardSourceTag.FROM_LITERAL); + notifyNewSource(source).tagAs(Tags.FROM_LITERAL); return source; } diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceTag.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceTag.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceTag.java Tue Apr 14 14:01:18 2015 +0200 @@ -27,16 +27,14 @@ /** * Categorical information (best implemented as enums} about particular sources of Guest Language * code that can be useful to configure behavior of both the language runtime and external tools. - * These might include {@linkplain StandardSourceTag standard tags} noting, for example, whether the + * These might include {@linkplain Source.Tags standard tags} noting, for example, whether the * source was read from a file and whether it should be considered library code. *

    - * An untagged {@link Source} should by default be considered application code. - *

    * The need for additional tags is likely to arise, in some cases because of issue specific to a * Guest Language, but also for help configuring the behavior of particular tools. * * @see Source - * @see StandardSourceTag + * @see Source.Tags */ public interface SourceTag { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/StandardSourceTag.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/StandardSourceTag.java Tue Apr 14 13:37:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 com.oracle.truffle.api.source; - -/** - * A general set of "properties" or "categories" that might be usefully attached to a particular - * source of code, both for use by the language runtime and by external tools. This set of tags - * includes some intended to be applied by default by {@link Source} factory methods or other - * services built into the Truffle platform. - *

    - * The need for additional tags is likely to arise, in some cases because of issue specific to a - * Guest Language, but also for help configuring the behavior of particular tools. - * - * @see Source - */ -public enum StandardSourceTag implements SourceTag { - - /** - * Builtin. - */ - BUILTIN("builtin", "implementation of language builtins"), - - /** - * From bytes. - */ - FROM_BYTES("bytes", "read from bytes"), - - /** - * Read from a file. - */ - FROM_FILE("file", "read from a file"), - - /** - * From literal text. - */ - FROM_LITERAL("literal", "from literal text"), - - /** - * From a {@linkplain java.io.Reader Reader}. - */ - FROM_READER("reader", "read from a Java Reader"), - - /** - * Read from a URL. - */ - FROM_URL("URL", "read from a URL"), - - /** - * Treat as LIBRARY code. - */ - LIBRARY("library", "library code"); - - private final String name; - private final String description; - - private StandardSourceTag(String name, String description) { - this.name = name; - this.description = description; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - -} diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/CoverageTracker.java Tue Apr 14 14:01:18 2015 +0200 @@ -35,10 +35,13 @@ /** * An {@link InstrumentationTool} that counts interpreter execution calls to AST nodes that - * hold a specified {@linkplain SyntaxTag tag}, tabulated by source and line number associated with - * each node. Tags are presumed to be applied external to the tool. If no tag is specified, - * {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to conventional - * behavior for code coverage tools. + * hold a specified {@linkplain SyntaxTag syntax tag}, tabulated by source and line number + * associated with each node. Syntax tags are presumed to be applied external to the tool. If no tag + * is specified, {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to + * conventional behavior for code coverage tools. + *

    + * No counts will be kept for execution in sources that hold the {@link SourceTag} + * {@link Tags#NO_COVERAGE}. *

    * Tool Life Cycle *

    @@ -48,7 +51,7 @@ *

    *

      *
    • "Execution call" on a node is is defined as invocation of a node method that is instrumented - * to produce the event {@link InstrumentListener#enter(Probe)};
    • + * to produce the event {@link SimpleInstrumentListener#enter(Probe)}; *
    • Execution calls are tabulated only at instrumented nodes, i.e. those for which * {@linkplain Node#isInstrumentable() isInstrumentable() == true};
    • *
    • Execution calls are tabulated only at nodes present in the AST when originally created; @@ -70,6 +73,30 @@ */ public final class CoverageTracker extends InstrumentationTool { + public enum Tags implements SourceTag { + + /** + * Report no counts for sources holding this tag. + */ + NO_COVERAGE("No Coverage", "Coverage Tracker will igore"); + + private final String name; + private final String description; + + private Tags(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + } + /** Counting data. */ private final Map coverageMap = new HashMap<>(); @@ -226,7 +253,7 @@ * A listener for events at each instrumented AST location. This listener counts * "execution calls" to the instrumented node. */ - private final class CoverageRecord extends DefaultInstrumentListener { + private final class CoverageRecord extends DefaultSimpleInstrumentListener { private final SourceSection srcSection; // The text of the code being counted private Instrument instrument; // The attached Instrument, in case need to remove. @@ -264,7 +291,7 @@ final SourceSection srcSection = probe.getProbedSourceSection(); if (srcSection == null) { // TODO (mlvdv) report this? - } else { + } else if (!srcSection.getSource().isTaggedAs(Tags.NO_COVERAGE)) { // Get the source line where the final LineLocation lineLocation = srcSection.getLineLocation(); CoverageRecord record = coverageMap.get(lineLocation); diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/tools/NodeExecCounter.java Tue Apr 14 14:01:18 2015 +0200 @@ -49,7 +49,7 @@ *

      *

        *
      • "Execution call" on a node is is defined as invocation of a node method that is instrumented - * to produce the event {@link ASTInstrumentListener#enter(Probe, Node, VirtualFrame)};
      • + * to produce the event {@link StandardInstrumentListener#enter(Probe, Node, VirtualFrame)}; *
      • Execution calls are tabulated only at instrumented nodes, i.e. those for which * {@linkplain Node#isInstrumentable() isInstrumentable() == true};
      • *
      • Execution calls are tabulated only at nodes present in the AST when originally created; @@ -95,7 +95,7 @@ * Listener for events at instrumented nodes. Counts are maintained in a shared table, so the * listener is stateless and can be shared by every {@link Instrument}. */ - private final ASTInstrumentListener instrumentListener = new DefaultASTInstrumentListener() { + private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() { @Override public void enter(Probe probe, Node node, VirtualFrame vFrame) { if (isEnabled()) { diff -r d9713313e88c -r c617a74a9eab graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Tue Apr 14 13:37:47 2015 +0200 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Tue Apr 14 14:01:18 2015 +0200 @@ -274,7 +274,7 @@ * attached at {@link SLWriteLocalVariableNode}, but provides no guards to protect it from being * attached elsewhere. */ - public final class SLPrintAssigmentValueListener extends DefaultInstrumentListener { + public final class SLPrintAssigmentValueListener extends DefaultSimpleInstrumentListener { private PrintStream output; diff -r d9713313e88c -r c617a74a9eab mx/mx_graal.py --- a/mx/mx_graal.py Tue Apr 14 13:37:47 2015 +0200 +++ b/mx/mx_graal.py Tue Apr 14 14:01:18 2015 +0200 @@ -1587,7 +1587,7 @@ for vmbuild in ['product', 'fastdebug']: for theVm in ['client', 'server']: if not isVMSupported(theVm): - mx.log('The' + theVm + ' VM is not supported on this platform') + mx.log('The ' + theVm + ' VM is not supported on this platform') continue with VM(theVm, vmbuild): with Task('DaCapo_pmd:' + theVm + ':' + vmbuild, tasks) as t: diff -r d9713313e88c -r c617a74a9eab src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Tue Apr 14 13:37:47 2015 +0200 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Tue Apr 14 14:01:18 2015 +0200 @@ -154,7 +154,7 @@ } } - return (jbyteArray) JNIHandles::make_local(reconstituted_code); + return (jbyteArray) JNIHandles::make_local(THREAD, reconstituted_code); C2V_END C2V_VMENTRY(jint, exceptionTableLength, (JNIEnv *, jobject, jlong metaspace_method)) @@ -576,7 +576,7 @@ } Handle result = java_lang_String::create_from_platform_dependent_str(st.as_string(), CHECK_NULL); - return JNIHandles::make_local(result()); + return JNIHandles::make_local(THREAD, result()); C2V_END C2V_VMENTRY(jobject, getStackTraceElement, (JNIEnv*, jobject, jlong metaspace_method, int bci)) @@ -585,7 +585,7 @@ methodHandle method = asMethod(metaspace_method); oop element = java_lang_StackTraceElement::create(method, bci, CHECK_NULL); - return JNIHandles::make_local(element); + return JNIHandles::make_local(THREAD, element); C2V_END C2V_VMENTRY(jobject, executeCompiledMethodVarargs, (JNIEnv*, jobject, jobject args, jobject hotspotInstalledCode)) @@ -609,7 +609,7 @@ if (jap.get_ret_type() == T_VOID) { return NULL; } else if (jap.get_ret_type() == T_OBJECT || jap.get_ret_type() == T_ARRAY) { - return JNIHandles::make_local((oop) result.get_jobject()); + return JNIHandles::make_local(THREAD, (oop) result.get_jobject()); } else { jvalue *value = (jvalue *) result.get_value_addr(); // Narrow the value down if required (Important on big endian machines) @@ -628,7 +628,7 @@ break; } oop o = java_lang_boxing_object::create(jap.get_ret_type(), value, CHECK_NULL); - return JNIHandles::make_local(o); + return JNIHandles::make_local(THREAD, o); } C2V_END @@ -656,7 +656,7 @@ i += 2; } - return (jlongArray) JNIHandles::make_local(result); + return (jlongArray) JNIHandles::make_local(THREAD, result); C2V_END C2V_VMENTRY(jlong, getLocalVariableTableStart, (JNIEnv *, jobject, jlong metaspace_method)) @@ -709,9 +709,9 @@ InstalledCode::set_address(hotspotInstalledCode, 0); C2V_END -C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv*, jobject, jlong metaspace_klass)) +C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv* env, jobject, jlong metaspace_klass)) Klass* klass = asKlass(metaspace_klass); - return JNIHandles::make_local(klass->java_mirror()); + return JNIHandles::make_local(THREAD, klass->java_mirror()); C2V_END C2V_VMENTRY(jlong, readUnsafeKlassPointer, (JNIEnv*, jobject, jobject o)) @@ -722,13 +722,13 @@ C2V_VMENTRY(jobject, readUncompressedOop, (JNIEnv*, jobject, jlong addr)) oop ret = oopDesc::load_decode_heap_oop((oop*)(address)addr); - return JNIHandles::make_local(ret); + return JNIHandles::make_local(THREAD, ret); C2V_END C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv*, jobject)) typeArrayOop arrayOop = oopFactory::new_longArray(GraalCounterSize, CHECK_NULL); JavaThread::collect_counters(arrayOop); - return (jlongArray) JNIHandles::make_local(arrayOop); + return (jlongArray) JNIHandles::make_local(THREAD, arrayOop); C2V_END C2V_VMENTRY(int, allocateCompileId, (JNIEnv*, jobject, jlong metaspace_method, int entry_bci)) diff -r d9713313e88c -r c617a74a9eab src/share/vm/graal/graalRuntime.cpp --- a/src/share/vm/graal/graalRuntime.cpp Tue Apr 14 13:37:47 2015 +0200 +++ b/src/share/vm/graal/graalRuntime.cpp Tue Apr 14 14:01:18 2015 +0200 @@ -647,7 +647,7 @@ TempNewSymbol sig = SymbolTable::new_symbol("()Lcom/oracle/truffle/api/TruffleRuntime;", CHECK_NULL); JavaValue result(T_OBJECT); JavaCalls::call_static(&result, klass, makeInstance, sig, CHECK_NULL); - return JNIHandles::make_local((oop) result.get_jobject()); + return JNIHandles::make_local(THREAD, (oop) result.get_jobject()); JVM_END // private static NativeFunctionInterfaceRuntime.createInterface() @@ -660,7 +660,7 @@ TempNewSymbol sig = SymbolTable::new_symbol("()Lcom/oracle/nfi/api/NativeFunctionInterface;", CHECK_NULL); JavaValue result(T_OBJECT); JavaCalls::call_static(&result, klass, makeInstance, sig, CHECK_NULL); - return JNIHandles::make_local((oop) result.get_jobject()); + return JNIHandles::make_local(THREAD, (oop) result.get_jobject()); JVM_END void GraalRuntime::check_generated_sources_sha1(TRAPS) {