# HG changeset patch # User Lukas Stadler # Date 1358850580 -3600 # Node ID 225002aba5a5fc498013234d682d1f8c068d272d # Parent 442668d41bc21629b5cd6f807c13d0f980cabbdb added new macro node facility, removed ArrayCopyIntrinsificationPhase diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/ArrayCopyIntrinsificationTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/ArrayCopyIntrinsificationTest.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/ArrayCopyIntrinsificationTest.java Tue Jan 22 11:29:40 2013 +0100 @@ -33,10 +33,7 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.test.*; import com.oracle.graal.graph.*; -import com.oracle.graal.hotspot.snippets.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.PhasePlan.PhasePosition; /** @@ -45,11 +42,6 @@ public class ArrayCopyIntrinsificationTest extends GraalCompilerTest { @Override - protected void editPhasePlan(ResolvedJavaMethod method, StructuredGraph graph, PhasePlan phasePlan) { - phasePlan.addPhase(PhasePosition.HIGH_LEVEL, new IntrinsifyArrayCopyPhase(runtime, new Assumptions(false))); - } - - @Override protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph) { int nodeCount = graph.getNodeCount(); InstalledCode result = super.getCode(method, graph); diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotSnippetInstaller.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotSnippetInstaller.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotSnippetInstaller.java Tue Jan 22 11:29:40 2013 +0100 @@ -42,7 +42,7 @@ } @Override - protected void installSubstitution(Method originalMethod, Method substituteMethod) { + protected void installMethodSubstitution(Method originalMethod, Method substituteMethod) { if (substituteMethod.getDeclaringClass() == IntegerSubstitutions.class || substituteMethod.getDeclaringClass() == LongSubstitutions.class) { if (substituteMethod.getName().equals("bitCount")) { if (!config.usePopCountInstruction) { @@ -58,6 +58,6 @@ assert config.cipherBlockChainingEncryptAESCryptStub != 0L; assert config.cipherBlockChainingDecryptAESCryptStub != 0L; } - super.installSubstitution(originalMethod, substituteMethod); + super.installMethodSubstitution(originalMethod, substituteMethod); } } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Tue Jan 22 11:29:40 2013 +0100 @@ -39,7 +39,6 @@ import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.hotspot.phases.*; -import com.oracle.graal.hotspot.snippets.*; import com.oracle.graal.java.*; import com.oracle.graal.nodes.*; import com.oracle.graal.phases.*; @@ -53,7 +52,6 @@ public class VMToCompilerImpl implements VMToCompiler { private final HotSpotGraalRuntime graalRuntime; - private IntrinsifyArrayCopyPhase intrinsifyArrayCopy; public final HotSpotResolvedPrimitiveType typeBoolean; public final HotSpotResolvedPrimitiveType typeChar; @@ -141,7 +139,6 @@ public void run() { // Snippets cannot have speculative optimizations since they have to be valid for the entire run of the VM. Assumptions assumptions = new Assumptions(false); - VMToCompilerImpl.this.intrinsifyArrayCopy = new IntrinsifyArrayCopyPhase(runtime, assumptions); SnippetInstaller installer = new HotSpotSnippetInstaller(runtime, assumptions, runtime.getGraalRuntime().getTarget()); GraalIntrinsics.installIntrinsics(installer); runtime.installSnippets(installer, assumptions); @@ -572,9 +569,6 @@ if (onStackReplacement) { phasePlan.addPhase(PhasePosition.AFTER_PARSING, new OnStackReplacementPhase()); } - if (GraalOptions.Intrinsify && GraalOptions.IntrinsifyArrayCopy) { - phasePlan.addPhase(PhasePosition.HIGH_LEVEL, intrinsifyArrayCopy); - } return phasePlan; } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopyNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopyNode.java Tue Jan 22 11:29:40 2013 +0100 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013, 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.hotspot.snippets; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.Node.IterableNodeType; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.virtual.*; +import com.oracle.graal.phases.common.*; +import com.oracle.graal.snippets.nodes.*; + +public class ArrayCopyNode extends MacroNode implements Virtualizable, IterableNodeType, Lowerable { + + public ArrayCopyNode(InvokeNode invoke) { + super(invoke); + } + + public ValueNode src() { + return arguments.get(0); + } + + public ValueNode srcPos() { + return arguments.get(1); + } + + public ValueNode dest() { + return arguments.get(2); + } + + public ValueNode destPos() { + return arguments.get(3); + } + + public ValueNode length() { + return arguments.get(4); + } + + @Override + public void lower(LoweringTool tool) { + ResolvedJavaMethod snippetMethod = null; + ResolvedJavaType srcType = src().objectStamp().type(); + ResolvedJavaType destType = dest().objectStamp().type(); + if (srcType != null && srcType.isArray() && destType != null && destType.isArray()) { + Kind componentKind = srcType.getComponentType().getKind(); + if (srcType.getComponentType() == destType.getComponentType()) { + snippetMethod = tool.getRuntime().lookupJavaMethod(ArrayCopySnippets.getSnippetForKind(componentKind)); + } else if (componentKind == Kind.Object && destType.getComponentType().isAssignableFrom(srcType.getComponentType())) { + snippetMethod = tool.getRuntime().lookupJavaMethod(ArrayCopySnippets.getSnippetForKind(Kind.Object)); + } + } + if (snippetMethod == null) { + snippetMethod = tool.getRuntime().lookupJavaMethod(ArrayCopySnippets.increaseGenericCallCounterMethod); + // we will call the generic method. the generic snippet will only increase the counter, not call the actual + // method. therefore we create a second invoke here. + ((StructuredGraph) graph()).addAfterFixed(this, createInvoke()); + } else { + Debug.log("%s > Intrinsify (%s)", Debug.currentScope(), snippetMethod.getSignature().getParameterType(0, snippetMethod.getDeclaringClass()).getComponentType()); + } + + if (snippetMethod != null) { + StructuredGraph snippetGraph = (StructuredGraph) snippetMethod.getCompilerStorage().get(Graph.class); + assert snippetGraph != null : "ArrayCopySnippets should be installed"; + InvokeNode invoke = replaceWithInvoke(); + InliningUtil.inline(invoke, snippetGraph, false); + } else { + super.lower(tool); + } + } + + @Override + public void virtualize(VirtualizerTool tool) { + if (srcPos().isConstant() && destPos().isConstant() && length().isConstant()) { + int srcPos = srcPos().asConstant().asInt(); + int destPos = destPos().asConstant().asInt(); + int length = length().asConstant().asInt(); + State srcState = tool.getObjectState(src()); + State destState = tool.getObjectState(dest()); + + if (srcState != null && srcState.getState() == EscapeState.Virtual && destState != null && destState.getState() == EscapeState.Virtual) { + VirtualObjectNode srcVirtual = srcState.getVirtualObject(); + VirtualObjectNode destVirtual = destState.getVirtualObject(); + if (length < 0) { + return; + } + if (srcPos < 0 || srcPos + length > srcVirtual.entryCount()) { + return; + } + if (destPos < 0 || destPos + length > destVirtual.entryCount()) { + return; + } + ResolvedJavaType destComponentType = destVirtual.type().getComponentType(); + if (destComponentType.getKind() == Kind.Object) { + for (int i = 0; i < length; i++) { + if (!destComponentType.isAssignableFrom(srcState.getEntry(srcPos + i).objectStamp().javaType(tool.getMetaAccessProvider()))) { + return; + } + } + } + for (int i = 0; i < length; i++) { + tool.setVirtualEntry(destState, destPos + i, srcState.getEntry(srcPos + i)); + } + tool.delete(); + Debug.log("virtualized arraycopyf(%s, %d, %s, %d, %d)", src(), srcPos, dest(), destPos, length); + } + } + } +} diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java Tue Jan 22 11:29:40 2013 +0100 @@ -25,8 +25,12 @@ import static com.oracle.graal.api.meta.DeoptimizationReason.*; import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*; +import java.lang.reflect.*; +import java.util.*; + import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.nodes.*; @@ -34,6 +38,7 @@ import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; +import com.oracle.graal.phases.*; import com.oracle.graal.snippets.*; import com.oracle.graal.snippets.Snippet.ConstantParameter; import com.oracle.graal.snippets.Snippet.Fold; @@ -42,6 +47,35 @@ @SuppressWarnings("unused") public class ArrayCopySnippets implements SnippetsInterface{ + + private static final EnumMap arraycopyMethods = new EnumMap<>(Kind.class); + public static final Method increaseGenericCallCounterMethod; + + private static void addArraycopySnippetMethod(Kind kind, Class arrayClass) throws NoSuchMethodException { + arraycopyMethods.put(kind, ArrayCopySnippets.class.getDeclaredMethod("arraycopy", arrayClass, int.class, arrayClass, int.class, int.class)); + } + + static { + try { + addArraycopySnippetMethod(Kind.Byte, byte[].class); + addArraycopySnippetMethod(Kind.Boolean, boolean[].class); + addArraycopySnippetMethod(Kind.Char, char[].class); + addArraycopySnippetMethod(Kind.Short, short[].class); + addArraycopySnippetMethod(Kind.Int, int[].class); + addArraycopySnippetMethod(Kind.Long, long[].class); + addArraycopySnippetMethod(Kind.Float, float[].class); + addArraycopySnippetMethod(Kind.Double, double[].class); + addArraycopySnippetMethod(Kind.Object, Object[].class); + increaseGenericCallCounterMethod = ArrayCopySnippets.class.getDeclaredMethod("increaseGenericCallCounter", Object.class, int.class, Object.class, int.class, int.class); + } catch (SecurityException | NoSuchMethodException e) { + throw new GraalInternalError(e); + } + } + + public static Method getSnippetForKind(Kind kind) { + return arraycopyMethods.get(kind); + } + private static final Kind VECTOR_KIND = Kind.Long; private static final long VECTOR_SIZE = arrayIndexScale(Kind.Long); @@ -75,40 +109,55 @@ public static void checkInputs(Object src, int srcPos, Object dest, int destPos, int length) { if (src == null || dest == null) { + checkNPECounter.inc(); throw new NullPointerException(); } if (srcPos < 0 || destPos < 0 || length < 0 || srcPos + length > ArrayLengthNode.arrayLength(src) || destPos + length > ArrayLengthNode.arrayLength(dest)) { + checkAIOOBECounter.inc(); throw new ArrayIndexOutOfBoundsException(); } + checkSuccessCounter.inc(); } @Snippet public static void arraycopy(byte[] src, int srcPos, byte[] dest, int destPos, int length) { + byteCounter.inc(); + vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Byte); + } + + @Snippet + public static void arraycopy(boolean[] src, int srcPos, boolean[] dest, int destPos, int length) { + booleanCounter.inc(); vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Byte); } @Snippet public static void arraycopy(char[] src, int srcPos, char[] dest, int destPos, int length) { + charCounter.inc(); vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Char); } @Snippet public static void arraycopy(short[] src, int srcPos, short[] dest, int destPos, int length) { + shortCounter.inc(); vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Short); } @Snippet public static void arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length) { + intCounter.inc(); vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Int); } @Snippet public static void arraycopy(float[] src, int srcPos, float[] dest, int destPos, int length) { + floatCounter.inc(); vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Float); } @Snippet public static void arraycopy(long[] src, int srcPos, long[] dest, int destPos, int length) { + longCounter.inc(); checkInputs(src, srcPos, dest, destPos, length); Kind baseKind = Kind.Long; int header = arrayBaseOffset(baseKind); @@ -130,6 +179,7 @@ @Snippet public static void arraycopy(double[] src, int srcPos, double[] dest, int destPos, int length) { + doubleCounter.inc(); checkInputs(src, srcPos, dest, destPos, length); Kind baseKind = Kind.Double; int header = arrayBaseOffset(baseKind); @@ -152,6 +202,7 @@ // Does NOT perform store checks @Snippet public static void arraycopy(Object[] src, int srcPos, Object[] dest, int destPos, int length) { + objectCounter.inc(); checkInputs(src, srcPos, dest, destPos, length); final int scale = arrayIndexScale(Kind.Object); int header = arrayBaseOffset(Kind.Object); @@ -180,4 +231,35 @@ } } } + + @Snippet + public static void increaseGenericCallCounter(Object src, int srcPos, Object dest, int destPos, int length) { + if (GraalOptions.SnippetCounters) { + if (src.getClass().getComponentType().isPrimitive()) { + genericPrimitiveCallCounter.inc(); + } else { + genericObjectCallCounter.inc(); + } + } + } + + private static final SnippetCounter.Group checkCounters = GraalOptions.SnippetCounters ? new SnippetCounter.Group("System.arraycopy checkInputs") : null; + private static final SnippetCounter checkSuccessCounter = new SnippetCounter(checkCounters, "checkSuccess", "checkSuccess"); + private static final SnippetCounter checkNPECounter = new SnippetCounter(checkCounters, "checkNPE", "checkNPE"); + private static final SnippetCounter checkAIOOBECounter = new SnippetCounter(checkCounters, "checkAIOOBE", "checkAIOOBE"); + + private static final SnippetCounter.Group counters = GraalOptions.SnippetCounters ? new SnippetCounter.Group("System.arraycopy") : null; + private static final SnippetCounter byteCounter = new SnippetCounter(counters, "byte[]", "arraycopy for byte[] arrays"); + private static final SnippetCounter charCounter = new SnippetCounter(counters, "char[]", "arraycopy for char[] arrays"); + private static final SnippetCounter shortCounter = new SnippetCounter(counters, "short[]", "arraycopy for short[] arrays"); + private static final SnippetCounter intCounter = new SnippetCounter(counters, "int[]", "arraycopy for int[] arrays"); + private static final SnippetCounter booleanCounter = new SnippetCounter(counters, "boolean[]", "arraycopy for boolean[] arrays"); + private static final SnippetCounter longCounter = new SnippetCounter(counters, "long[]", "arraycopy for long[] arrays"); + private static final SnippetCounter objectCounter = new SnippetCounter(counters, "Object[]", "arraycopy for Object[] arrays"); + private static final SnippetCounter floatCounter = new SnippetCounter(counters, "float[]", "arraycopy for float[] arrays"); + private static final SnippetCounter doubleCounter = new SnippetCounter(counters, "double[]", "arraycopy for double[] arrays"); + private static final SnippetCounter genericPrimitiveCallCounter = new SnippetCounter(counters, "genericPrimitive", "call to the generic, native arraycopy method"); + private static final SnippetCounter genericObjectCallCounter = new SnippetCounter(counters, "genericObject", "call to the generic, native arraycopy method"); + + } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java Mon Jan 21 12:23:55 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2011, 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.hotspot.snippets; - -import java.lang.reflect.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.phases.*; -import com.oracle.graal.phases.common.*; - -public class IntrinsifyArrayCopyPhase extends Phase { - private final GraalCodeCacheProvider runtime; - private final Assumptions assumptions; - private ResolvedJavaMethod arrayCopy; - private ResolvedJavaMethod byteArrayCopy; - private ResolvedJavaMethod shortArrayCopy; - private ResolvedJavaMethod charArrayCopy; - private ResolvedJavaMethod intArrayCopy; - private ResolvedJavaMethod longArrayCopy; - private ResolvedJavaMethod floatArrayCopy; - private ResolvedJavaMethod doubleArrayCopy; - private ResolvedJavaMethod objectArrayCopy; - - public IntrinsifyArrayCopyPhase(GraalCodeCacheProvider runtime, Assumptions assumptions) { - this.runtime = runtime; - this.assumptions = assumptions; - try { - byteArrayCopy = getArrayCopySnippet(runtime, byte.class); - charArrayCopy = getArrayCopySnippet(runtime, char.class); - shortArrayCopy = getArrayCopySnippet(runtime, short.class); - intArrayCopy = getArrayCopySnippet(runtime, int.class); - longArrayCopy = getArrayCopySnippet(runtime, long.class); - floatArrayCopy = getArrayCopySnippet(runtime, float.class); - doubleArrayCopy = getArrayCopySnippet(runtime, double.class); - objectArrayCopy = getArrayCopySnippet(runtime, Object.class); - arrayCopy = runtime.lookupJavaMethod(System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class)); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - } - - private static ResolvedJavaMethod getArrayCopySnippet(CodeCacheProvider runtime, Class componentClass) throws NoSuchMethodException { - Class arrayClass = Array.newInstance(componentClass, 0).getClass(); - return runtime.lookupJavaMethod(ArrayCopySnippets.class.getDeclaredMethod("arraycopy", arrayClass, int.class, arrayClass, int.class, int.class)); - } - - @Override - protected void run(StructuredGraph graph) { - boolean hits = false; - for (MethodCallTargetNode methodCallTarget : graph.getNodes(MethodCallTargetNode.class)) { - ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod(); - ResolvedJavaMethod snippetMethod = null; - if (targetMethod == arrayCopy) { - ValueNode src = methodCallTarget.arguments().get(0); - ValueNode dest = methodCallTarget.arguments().get(2); - assert src != null && dest != null; - ResolvedJavaType srcType = src.objectStamp().type(); - ResolvedJavaType destType = dest.objectStamp().type(); - if (srcType != null - && srcType.isArray() - && destType != null - && destType.isArray()) { - Kind componentKind = srcType.getComponentType().getKind(); - if (srcType.getComponentType() == destType.getComponentType()) { - if (componentKind == Kind.Int) { - snippetMethod = intArrayCopy; - } else if (componentKind == Kind.Char) { - snippetMethod = charArrayCopy; - } else if (componentKind == Kind.Long) { - snippetMethod = longArrayCopy; - } else if (componentKind == Kind.Byte) { - snippetMethod = byteArrayCopy; - } else if (componentKind == Kind.Short) { - snippetMethod = shortArrayCopy; - } else if (componentKind == Kind.Float) { - snippetMethod = floatArrayCopy; - } else if (componentKind == Kind.Double) { - snippetMethod = doubleArrayCopy; - } else if (componentKind == Kind.Object) { - snippetMethod = objectArrayCopy; - } - } else if (componentKind == Kind.Object - && destType.getComponentType().isAssignableFrom(srcType.getComponentType())) { - snippetMethod = objectArrayCopy; - } - } - } - - if (snippetMethod != null) { - StructuredGraph snippetGraph = (StructuredGraph) snippetMethod.getCompilerStorage().get(Graph.class); - assert snippetGraph != null : "ArrayCopySnippets should be installed"; - hits = true; - Debug.log("%s > Intrinsify (%s)", Debug.currentScope(), snippetMethod.getSignature().getParameterType(0, snippetMethod.getDeclaringClass()).getComponentType()); - InliningUtil.inline(methodCallTarget.invoke(), snippetGraph, false); - } else { - Debug.log("%s > not intrinsifying arraycopy", Debug.currentScope()); - } - } - if (GraalOptions.OptCanonicalizer && hits) { - new CanonicalizerPhase(null, runtime, assumptions).apply(graph); - } - } -} diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/SystemSubstitutions.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/SystemSubstitutions.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/SystemSubstitutions.java Tue Jan 22 11:29:40 2013 +0100 @@ -31,6 +31,7 @@ import com.oracle.graal.hotspot.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.snippets.*; +import com.oracle.graal.snippets.ClassSubstitution.MacroSubstitution; import com.oracle.graal.snippets.ClassSubstitution.MethodSubstitution; import com.oracle.graal.word.*; @@ -43,6 +44,9 @@ public static final Descriptor JAVA_TIME_MILLIS = new Descriptor("javaTimeMillis", false, long.class); public static final Descriptor JAVA_TIME_NANOS = new Descriptor("javaTimeNanos", false, long.class); + @MacroSubstitution(macro = ArrayCopyNode.class, isStatic = true) + public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); + @MethodSubstitution public static long currentTimeMillis() { return callLong(JAVA_TIME_MILLIS); diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractStateSplit.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractStateSplit.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractStateSplit.java Tue Jan 22 11:29:40 2013 +0100 @@ -48,4 +48,9 @@ public AbstractStateSplit(Stamp stamp) { super(stamp); } + + public AbstractStateSplit(Stamp stamp, FrameState stateAfter) { + super(stamp); + this.stateAfter = stateAfter; + } } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java Tue Jan 22 11:29:40 2013 +0100 @@ -131,6 +131,10 @@ return this; } + public JavaType returnType() { + return returnType; + } + @Override public Stamp returnStamp() { Kind returnKind = targetMethod.getSignature().getReturnKind(); diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Tue Jan 22 11:29:40 2013 +0100 @@ -200,16 +200,36 @@ return computeInliningLevel(invoke); } - protected static StructuredGraph getGraph(final ResolvedJavaMethod concrete, final InliningCallback callback) { + protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, InliningCallback callback, Assumptions assumptions, boolean receiverNullCheck) { + Class< ? extends FixedWithNextNode> macroNodeClass = getMacroNodeClass(concrete); + if (macroNodeClass != null) { + StructuredGraph graph = (StructuredGraph) invoke.graph(); + assert invoke instanceof InvokeNode; + FixedWithNextNode macroNode; + try { + macroNode = macroNodeClass.getConstructor(InvokeNode.class).newInstance(invoke); + } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { + throw new GraalInternalError(e).addContext(invoke.node()).addContext("macroSubstitution", macroNodeClass); + } + CallTargetNode callTarget = invoke.callTarget(); + graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode)); + GraphUtil.killWithUnusedFloatingInputs(callTarget); + } else { + StructuredGraph calleeGraph = getIntrinsicGraph(concrete); + if (calleeGraph == null) { + calleeGraph = getGraph(concrete, callback); + } + assumptions.recordMethodContents(concrete); + InliningUtil.inline(invoke, calleeGraph, receiverNullCheck); + } + } + + private static StructuredGraph getGraph(final ResolvedJavaMethod concrete, final InliningCallback callback) { return Debug.scope("GetInliningGraph", concrete, new Callable() { @Override public StructuredGraph call() throws Exception { - StructuredGraph result = getIntrinsicGraph(concrete); - if (result == null) { - assert !Modifier.isNative(concrete.getModifiers()); - result = callback.buildGraph(concrete); - } - return result; + assert !Modifier.isNative(concrete.getModifiers()); + return callback.buildGraph(concrete); } }); } @@ -229,9 +249,7 @@ @Override public void inline(StructuredGraph compilerGraph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { - StructuredGraph graph = getGraph(concrete, callback); - assumptions.recordMethodContents(concrete); - InliningUtil.inline(invoke, graph, true); + inline(invoke, concrete, callback, assumptions, true); } @Override @@ -283,9 +301,7 @@ graph.addBeforeFixed(invoke.node(), guard); graph.addBeforeFixed(invoke.node(), anchor); - StructuredGraph calleeGraph = getGraph(concrete, callback); - assumptions.recordMethodContents(concrete); - InliningUtil.inline(invoke, calleeGraph, false); + inline(invoke, concrete, callback, assumptions, false); } @Override @@ -405,14 +421,7 @@ GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); } - // get all graphs and record assumptions assert invoke.node().isAlive(); - StructuredGraph[] calleeGraphs = new StructuredGraph[numberOfMethods]; - for (int i = 0; i < numberOfMethods; i++) { - ResolvedJavaMethod concrete = concretes.get(i); - calleeGraphs[i] = getGraph(concrete, callback); - assumptions.recordMethodContents(concrete); - } // replace the invoke with a switch on the type of the actual receiver Kind hubKind = invoke.methodCallTarget().targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); @@ -439,8 +448,8 @@ PiNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); - StructuredGraph calleeGraph = calleeGraphs[i]; - InliningUtil.inline(invokeForInlining, calleeGraph, false); + inline(invokeForInlining, concretes.get(i), callback, assumptions, false); + replacements.add(anchoredReceiver); } if (shouldFallbackToInvoke()) { @@ -512,9 +521,7 @@ calleeEntryNode.setNext(invoke.node()); ResolvedJavaMethod concrete = concretes.get(0); - StructuredGraph calleeGraph = getGraph(concrete, callback); - assumptions.recordMethodContents(concrete); - InliningUtil.inline(invoke, calleeGraph, false); + inline(invoke, concrete, callback, assumptions, false); } private FixedNode createDispatchOnType(StructuredGraph graph, LoadHubNode hub, BeginNode[] successors) { @@ -1043,8 +1050,7 @@ } public static boolean canIntrinsify(ResolvedJavaMethod target) { - StructuredGraph intrinsicGraph = getIntrinsicGraph(target); - return intrinsicGraph != null; + return getIntrinsicGraph(target) != null || getMacroNodeClass(target) != null; } public static StructuredGraph getIntrinsicGraph(ResolvedJavaMethod target) { @@ -1058,4 +1064,9 @@ return target.getCompiledCodeSize(); } } + + public static Class< ? extends FixedWithNextNode> getMacroNodeClass(ResolvedJavaMethod target) { + Object result = target.getCompilerStorage().get(Node.class); + return result == null ? null : ((Class< ? >) result).asSubclass(FixedWithNextNode.class); + } } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/OptimisticOptimizations.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/OptimisticOptimizations.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/OptimisticOptimizations.java Tue Jan 22 11:29:40 2013 +0100 @@ -27,18 +27,14 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.debug.*; - +public final class OptimisticOptimizations { -public final class OptimisticOptimizations { public static final OptimisticOptimizations ALL = new OptimisticOptimizations(EnumSet.allOf(Optimization.class)); public static final OptimisticOptimizations NONE = new OptimisticOptimizations(EnumSet.noneOf(Optimization.class)); private static final DebugMetric disabledOptimisticOptsMetric = Debug.metric("DisabledOptimisticOpts"); private static enum Optimization { - RemoveNeverExecutedCode, - UseTypeCheckedInlining, - UseTypeCheckHints, - UseExceptionProbability + RemoveNeverExecutedCode, UseTypeCheckedInlining, UseTypeCheckHints, UseExceptionProbability } private final Set enabledOpts; @@ -56,7 +52,8 @@ if (checkDeoptimizations(method.getProfilingInfo(), deoptReason)) { enabledOpts.add(optimization); } else { - // TODO (chaeubl): change to Debug.log when we are sure that optimistic optimizations are not disabled unnecessarily + // TODO (chaeubl): change to Debug.log when we are sure that optimistic optimizations are not disabled + // unnecessarily TTY.println("WARN: deactivated optimistic optimization %s for %s", optimization.name(), MetaUtil.format("%H.%n(%p)", method)); disabledOptimisticOptsMetric.increment(); } @@ -91,7 +88,7 @@ } public boolean lessOptimisticThan(OptimisticOptimizations other) { - for (Optimization opt: Optimization.values()) { + for (Optimization opt : Optimization.values()) { if (!enabledOpts.contains(opt) && other.enabledOpts.contains(opt)) { return true; } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java --- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java Tue Jan 22 11:29:40 2013 +0100 @@ -25,10 +25,12 @@ import java.lang.annotation.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.snippets.nodes.*; /** - * Denotes a class that substitutes methods of another specified class. - * The substitute methods are exactly those annotated by {@link MethodSubstitution}. + * Denotes a class that substitutes methods of another specified class. The substitute methods are exactly those + * annotated by {@link MethodSubstitution}. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @@ -37,34 +39,35 @@ /** * Specifies the original class. *

- * If the default value is specified for this element, then a non-default - * value must be given for the {@link #className()} element. - */ - Class value() default ClassSubstitution.class; + * If the default value is specified for this element, then a non-default value must be given for the + * {@link #className()} element. + */ + Class< ? > value() default ClassSubstitution.class; /** * Specifies the original class. *

- * This method is provided for cases where the original class - * is not accessible (according to Java language access control rules). + * This method is provided for cases where the original class is not accessible (according to Java language access + * control rules). *

- * If the default value is specified for this element, then a non-default - * value must be given for the {@link #value()} element. + * If the default value is specified for this element, then a non-default value must be given for the + * {@link #value()} element. */ String className() default ""; /** - * Denotes a substitute method. A substitute method can call the original/substituted - * method by making a recursive call to itself. + * Denotes a substitute method. A substitute method can call the original/substituted method by making a recursive + * call to itself. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MethodSubstitution { + /** * Gets the name of the original method. *

- * If the default value is specified for this element, then the - * name of the original method is same as the substitute method. + * If the default value is specified for this element, then the name of the original method is same as the + * substitute method. */ String value() default ""; @@ -76,9 +79,49 @@ /** * Gets the {@linkplain Signature#getMethodDescriptor() signature} of the original method. *

- * If the default value is specified for this element, then the - * signature of the original method is the same as the substitute method. + * If the default value is specified for this element, then the signature of the original method is the same as + * the substitute method. */ String signature() default ""; } + + /** + * Denotes a macro substitute method. This replaces a method invocation with an instance of the specified node + * class. + * + * A macro substitution can be combined with a normal substitution, so that the macro node can be replaced with the + * actual substitution code during lowering. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface MacroSubstitution { + + /** + * Gets the name of the substituted method. + *

+ * If the default value is specified for this element, then the name of the substituted method is same as the + * substitute method. + */ + String value() default ""; + + /** + * Determines if the substituted method is static. + */ + boolean isStatic() default true; + + /** + * Gets the {@linkplain Signature#getMethodDescriptor() signature} of the substituted method. + *

+ * If the default value is specified for this element, then the signature of the substituted method is the same + * as the substitute method. + */ + String signature() default ""; + + /** + * The node class with which the method invocation should be replaced. It needs to be a subclass of + * {@link FixedWithNextNode}, and it is expected to provide a public constructor that takes an InvokeNode as a + * parameter. For most cases this class should subclass {@link MacroNode} and use its constructor. + */ + Class< ? extends FixedWithNextNode> macro(); + } } diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java --- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java Mon Jan 21 12:23:55 2013 +0100 +++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java Tue Jan 22 11:29:40 2013 +0100 @@ -41,6 +41,7 @@ import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.snippets.ClassSubstitution.MacroSubstitution; import com.oracle.graal.snippets.ClassSubstitution.MethodSubstitution; import com.oracle.graal.snippets.Snippet.DefaultSnippetInliningPolicy; import com.oracle.graal.snippets.Snippet.SnippetInliningPolicy; @@ -107,21 +108,31 @@ ClassSubstitution classSubstitution = substitutions.getAnnotation(ClassSubstitution.class); for (Method substituteMethod : substitutions.getDeclaredMethods()) { MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class); - if (methodSubstitution == null) { + MacroSubstitution macroSubstitution = substituteMethod.getAnnotation(MacroSubstitution.class); + if (methodSubstitution == null && macroSubstitution == null) { continue; } int modifiers = substituteMethod.getModifiers(); if (!Modifier.isStatic(modifiers)) { throw new RuntimeException("Substitution methods must be static: " + substituteMethod); - } else if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { - throw new RuntimeException("Substitution method must not be abstract or native: " + substituteMethod); } - String originalName = originalName(substituteMethod, methodSubstitution); - Class[] originalParameters = originalParameters(substituteMethod, methodSubstitution); - Method originalMethod = originalMethod(classSubstitution, originalName, originalParameters); - installSubstitution(originalMethod, substituteMethod); + if (methodSubstitution != null) { + if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { + throw new RuntimeException("Substitution method must not be abstract or native: " + substituteMethod); + } + String originalName = originalName(substituteMethod, methodSubstitution.value()); + Class[] originalParameters = originalParameters(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic()); + Method originalMethod = originalMethod(classSubstitution, originalName, originalParameters); + installMethodSubstitution(originalMethod, substituteMethod); + } + if (macroSubstitution != null) { + String originalName = originalName(substituteMethod, macroSubstitution.value()); + Class[] originalParameters = originalParameters(substituteMethod, macroSubstitution.signature(), macroSubstitution.isStatic()); + Method originalMethod = originalMethod(classSubstitution, originalName, originalParameters); + installMacroSubstitution(originalMethod, macroSubstitution.macro()); + } } } @@ -136,7 +147,7 @@ * @param originalMethod a method being substituted * @param substituteMethod the substitute method */ - protected void installSubstitution(Method originalMethod, Method substituteMethod) { + protected void installMethodSubstitution(Method originalMethod, Method substituteMethod) { substitute = runtime.lookupJavaMethod(substituteMethod); original = runtime.lookupJavaMethod(originalMethod); try { @@ -151,8 +162,20 @@ } } + /** + * Installs a macro substitution. + * + * @param originalMethod a method being substituted + * @param macro the substitute macro node class + */ + protected void installMacroSubstitution(Method originalMethod, Class< ? extends FixedWithNextNode> macro) { + ResolvedJavaMethod originalJavaMethod = runtime.lookupJavaMethod(originalMethod); + Object oldValue = originalJavaMethod.getCompilerStorage().put(Node.class, macro); + assert oldValue == null; + } + private SnippetInliningPolicy inliningPolicy(ResolvedJavaMethod method) { - Class policyClass = SnippetInliningPolicy.class; + Class< ? extends SnippetInliningPolicy> policyClass = SnippetInliningPolicy.class; Snippet snippet = method.getAnnotation(Snippet.class); if (snippet != null) { policyClass = snippet.inlining(); @@ -263,12 +286,12 @@ return graph; } - private static String originalName(Method substituteMethod, MethodSubstitution methodSubstitution) { - String name = substituteMethod.getName(); - if (!methodSubstitution.value().isEmpty()) { - name = methodSubstitution.value(); + private static String originalName(Method substituteMethod, String methodSubstitution) { + if (methodSubstitution.isEmpty()) { + return substituteMethod.getName(); + } else { + return methodSubstitution; } - return name; } private static Class resolveType(String className) { @@ -294,16 +317,16 @@ return dimensions == 0 ? baseClass : Array.newInstance(baseClass, new int[dimensions]).getClass(); } - private Class[] originalParameters(Method substituteMethod, MethodSubstitution methodSubstitution) { + private Class[] originalParameters(Method substituteMethod, String methodSubstitution, boolean isStatic) { Class[] parameters; - if (methodSubstitution.signature().isEmpty()) { + if (methodSubstitution.isEmpty()) { parameters = substituteMethod.getParameterTypes(); - if (!methodSubstitution.isStatic()) { + if (!isStatic) { assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter"; parameters = Arrays.copyOfRange(parameters, 1, parameters.length); } } else { - Signature signature = runtime.parseMethodDescriptor(methodSubstitution.signature()); + Signature signature = runtime.parseMethodDescriptor(methodSubstitution); parameters = new Class[signature.getParameterCount(false)]; for (int i = 0; i < parameters.length; i++) { parameters[i] = resolveType(signature.getParameterType(i, null)); diff -r 442668d41bc2 -r 225002aba5a5 graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/MacroNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/MacroNode.java Tue Jan 22 11:29:40 2013 +0100 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013, 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.snippets.nodes; + +import java.lang.reflect.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.graal.nodes.spi.*; + +public class MacroNode extends AbstractStateSplit implements Lowerable { + + @Input protected NodeInputList arguments; + + private final int bci; + private final ResolvedJavaMethod targetMethod; + private final JavaType returnType; + + protected MacroNode(InvokeNode invoke) { + super(invoke.stamp(), invoke.stateAfter()); + this.arguments = new NodeInputList<>(this, invoke.methodCallTarget().arguments()); + this.bci = invoke.bci(); + this.targetMethod = invoke.methodCallTarget().targetMethod(); + this.returnType = invoke.methodCallTarget().returnType(); + } + + @Override + public void lower(LoweringTool tool) { + replaceWithInvoke(); + } + + public InvokeNode replaceWithInvoke() { + InvokeNode invoke = createInvoke(); + + ((StructuredGraph) graph()).replaceFixedWithFixed(this, invoke); + return invoke; + } + + protected InvokeNode createInvoke() { + InvokeKind invokeKind = Modifier.isStatic(targetMethod.getModifiers()) ? InvokeKind.Static : InvokeKind.Special; + MethodCallTargetNode callTarget = graph().add(new MethodCallTargetNode(invokeKind, targetMethod, arguments.toArray(new ValueNode[arguments.size()]), returnType)); + InvokeNode invoke = graph().add(new InvokeNode(callTarget, bci, StructuredGraph.INVALID_GRAPH_ID)); + invoke.setStateAfter(stateAfter()); + return invoke; + } +}