blob: 08eab361382de9720905c70323924e23ce5c7e44 [file] [log] [blame]
/*
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.hotspot.replacements.arraycopy;
import static org.graalvm.compiler.core.common.GraalOptions.SnippetCounters;
import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.KLASS_SUPER_CHECK_OFFSET_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayBaseOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayClassElementOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.arrayIndexScale;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.layoutHelperElementTypePrimitiveInPlace;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.loadHub;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.readLayoutHelper;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.superCheckOffsetOffset;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.SLOW_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
import java.lang.reflect.Method;
import java.util.EnumMap;
import java.util.Map;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
import org.graalvm.compiler.core.common.LocationIdentity;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
import org.graalvm.compiler.hotspot.word.KlassPointer;
import org.graalvm.compiler.nodes.CallTargetNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.DeoptimizeNode;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.NamedLocationIdentity;
import org.graalvm.compiler.nodes.PiNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.extended.UnsafeLoadNode;
import org.graalvm.compiler.nodes.java.ArrayLengthNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.compiler.replacements.SnippetCounter;
import org.graalvm.compiler.replacements.SnippetTemplate;
import org.graalvm.compiler.replacements.SnippetTemplate.Arguments;
import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo;
import org.graalvm.compiler.replacements.Snippets;
import org.graalvm.compiler.replacements.nodes.BasicArrayCopyNode;
import org.graalvm.compiler.replacements.nodes.DirectObjectStoreNode;
import org.graalvm.compiler.replacements.nodes.ExplodeLoopNode;
import org.graalvm.compiler.word.Word;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
import jdk.vm.ci.meta.DeoptimizationAction;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
public class ArrayCopySnippets implements Snippets {
private static int checkArrayType(KlassPointer hub) {
int layoutHelper = readLayoutHelper(hub);
if (probability(SLOW_PATH_PROBABILITY, layoutHelper >= 0)) {
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
return layoutHelper;
}
private static void checkLimits(Object src, int srcPos, Object dest, int destPos, int length) {
if (probability(SLOW_PATH_PROBABILITY, srcPos < 0)) {
checkAIOOBECounter.inc();
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
if (probability(SLOW_PATH_PROBABILITY, destPos < 0)) {
checkAIOOBECounter.inc();
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
if (probability(SLOW_PATH_PROBABILITY, length < 0)) {
checkAIOOBECounter.inc();
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
if (probability(SLOW_PATH_PROBABILITY, srcPos > ArrayLengthNode.arrayLength(src) - length)) {
checkAIOOBECounter.inc();
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
if (probability(SLOW_PATH_PROBABILITY, destPos > ArrayLengthNode.arrayLength(dest) - length)) {
checkAIOOBECounter.inc();
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
checkSuccessCounter.inc();
}
@Snippet
public static void arraycopyZeroLengthIntrinsic(Object src, int srcPos, Object dest, int destPos, int length) {
Object nonNullSrc = GraalDirectives.guardingNonNull(src);
Object nonNullDest = GraalDirectives.guardingNonNull(dest);
KlassPointer srcHub = loadHub(nonNullSrc);
KlassPointer destHub = loadHub(nonNullDest);
checkArrayType(srcHub);
checkArrayType(destHub);
checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
zeroLengthStaticCounter.inc();
}
@Snippet
public static void arraycopyExactIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter JavaKind elementKind, @ConstantParameter SnippetCounter counter,
@ConstantParameter SnippetCounter copiedCounter) {
Object nonNullSrc = GraalDirectives.guardingNonNull(src);
Object nonNullDest = GraalDirectives.guardingNonNull(dest);
checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
counter.inc();
copiedCounter.add(length);
ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind);
if (length == 0) {
zeroLengthDynamicCounter.inc();
} else {
nonZeroLengthDynamicCounter.inc();
nonZeroLengthDynamicCopiedCounter.add(length);
}
}
/**
* This intrinsic is useful for the case where we know something statically about one of the
* inputs but not the other.
*/
@Snippet
public static void arraycopyPredictedExactIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter JavaKind elementKind,
@ConstantParameter SnippetCounter counter, @ConstantParameter SnippetCounter copiedCounter) {
Object nonNullSrc = GraalDirectives.guardingNonNull(src);
Object nonNullDest = GraalDirectives.guardingNonNull(dest);
KlassPointer srcHub = loadHub(nonNullSrc);
KlassPointer destHub = loadHub(nonNullDest);
if (probability(SLOW_PATH_PROBABILITY, srcHub != destHub)) {
DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
}
checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
counter.inc();
copiedCounter.add(length);
ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind);
if (length == 0) {
zeroLengthDynamicCounter.inc();
} else {
nonZeroLengthDynamicCounter.inc();
nonZeroLengthDynamicCopiedCounter.add(length);
}
}
@Snippet
public static void arraycopyPredictedObjectWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, KlassPointer objectArrayKlass,
@ConstantParameter SnippetCounter counter, @ConstantParameter SnippetCounter copiedCounter) {
if (length > 0) {
KlassPointer srcHub = loadHub(PiNode.asNonNullObject(nonNullSrc));
KlassPointer destHub = loadHub(PiNode.asNonNullObject(nonNullDest));
if (probability(FAST_PATH_PROBABILITY, srcHub == destHub || destHub == objectArrayKlass)) {
counter.inc();
copiedCounter.add(length);
predictedObjectArrayCopyFastPathCounter.inc();
predictedObjectArrayCopyFastPathCopiedCounter.add(length);
ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length);
} else {
predictedObjectArrayCopySlowPathCounter.inc();
predictedObjectArrayCopySlowPathCopiedCounter.add(length);
System.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length);
}
}
}
/**
* This is the basic template for the full arraycopy checks, including a check that the
* underlying type is really an array type.
*/
@Snippet
public static void arraycopySlowPathIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, KlassPointer predictedKlass, @ConstantParameter JavaKind elementKind,
@ConstantParameter SnippetInfo slowPath, @ConstantParameter Object slowPathArgument) {
Object nonNullSrc = GraalDirectives.guardingNonNull(src);
Object nonNullDest = GraalDirectives.guardingNonNull(dest);
KlassPointer srcHub = loadHub(nonNullSrc);
KlassPointer destHub = loadHub(nonNullDest);
checkArrayType(srcHub);
checkArrayType(destHub);
checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
if (length == 0) {
zeroLengthDynamicCounter.inc();
} else {
nonZeroLengthDynamicCounter.inc();
nonZeroLengthDynamicCopiedCounter.add(length);
}
ArrayCopySlowPathNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, predictedKlass, elementKind, slowPath, slowPathArgument);
}
/**
* Snippet for unrolled arraycopy.
*/
@Snippet
public static void arraycopyUnrolledIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter int unrolledLength, @ConstantParameter JavaKind elementKind) {
Object nonNullSrc = GraalDirectives.guardingNonNull(src);
Object nonNullDest = GraalDirectives.guardingNonNull(dest);
checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
if (length == 0) {
zeroLengthDynamicCounter.inc();
} else {
nonZeroLengthDynamicCounter.inc();
nonZeroLengthDynamicCopiedCounter.add(length);
}
ArrayCopyUnrollNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, unrolledLength, elementKind);
}
@Snippet
public static void checkcastArraycopyWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length) {
if (length > 0) {
KlassPointer destKlass = loadHub(nonNullDest);
KlassPointer srcKlass = loadHub(nonNullSrc);
if (probability(SLOW_PATH_PROBABILITY, srcKlass == destKlass)) {
// no storecheck required.
objectCheckcastSameTypeCounter.inc();
objectCheckcastSameTypeCopiedCounter.add(length);
ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length);
} else {
KlassPointer destElemKlass = destKlass.readKlassPointer(arrayClassElementOffset(INJECTED_VMCONFIG), OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION);
Word superCheckOffset = Word.signed(destElemKlass.readInt(superCheckOffsetOffset(INJECTED_VMCONFIG), KLASS_SUPER_CHECK_OFFSET_LOCATION));
objectCheckcastCounter.inc();
objectCheckcastCopiedCounter.add(length);
int copiedElements = CheckcastArrayCopyCallNode.checkcastArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, superCheckOffset, destElemKlass, false);
if (copiedElements != 0) {
/*
* the checkcast stub doesn't throw the ArrayStoreException, but returns the
* number of copied elements (xor'd with -1).
*/
copiedElements ^= -1;
System.arraycopy(nonNullSrc, srcPos + copiedElements, nonNullDest, destPos + copiedElements, length - copiedElements);
}
}
}
}
@Snippet
public static void arraycopyGeneric(Object src, int srcPos, Object dest, int destPos, int length) {
Object nonNullSrc = GraalDirectives.guardingNonNull(src);
Object nonNullDest = GraalDirectives.guardingNonNull(dest);
KlassPointer srcHub = loadHub(nonNullSrc);
KlassPointer destHub = loadHub(nonNullDest);
if (probability(FAST_PATH_PROBABILITY, srcHub.equal(destHub)) && probability(FAST_PATH_PROBABILITY, nonNullSrc != nonNullDest)) {
int layoutHelper = checkArrayType(srcHub);
final boolean isObjectArray = ((layoutHelper & layoutHelperElementTypePrimitiveInPlace(INJECTED_VMCONFIG)) == 0);
checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
if (probability(FAST_PATH_PROBABILITY, isObjectArray)) {
genericObjectExactCallCounter.inc();
genericObjectExactCallCopiedCounter.add(length);
ArrayCopyCallNode.disjointArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, JavaKind.Object);
} else {
genericPrimitiveCallCounter.inc();
genericPrimitiveCallCopiedCounter.add(length);
UnsafeArrayCopyNode.arraycopyPrimitive(nonNullSrc, srcPos, nonNullDest, destPos, length, layoutHelper);
}
} else {
SystemArraycopyCounter.inc();
SystemArraycopyCopiedCounter.add(length);
System.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length);
}
}
@Fold
static LocationIdentity getArrayLocation(JavaKind kind) {
return NamedLocationIdentity.getArrayLocation(kind);
}
@Snippet
public static void arraycopyUnrolledWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, @ConstantParameter int length, @ConstantParameter JavaKind elementKind) {
final int scale = arrayIndexScale(elementKind);
int arrayBaseOffset = arrayBaseOffset(elementKind);
LocationIdentity arrayLocation = getArrayLocation(elementKind);
if (nonNullSrc == nonNullDest && srcPos < destPos) { // bad aliased case
long start = (long) (length - 1) * scale;
long i = start;
ExplodeLoopNode.explodeLoop();
for (int iteration = 0; iteration < length; iteration++) {
if (i >= 0) {
Object a = UnsafeLoadNode.load(nonNullSrc, arrayBaseOffset + i + (long) srcPos * scale, elementKind, arrayLocation);
DirectObjectStoreNode.storeObject(nonNullDest, arrayBaseOffset, i + (long) destPos * scale, a, arrayLocation, elementKind);
i -= scale;
}
}
} else {
long end = (long) length * scale;
long i = 0;
ExplodeLoopNode.explodeLoop();
for (int iteration = 0; iteration < length; iteration++) {
if (i < end) {
Object a = UnsafeLoadNode.load(nonNullSrc, arrayBaseOffset + i + (long) srcPos * scale, elementKind, arrayLocation);
DirectObjectStoreNode.storeObject(nonNullDest, arrayBaseOffset, i + (long) destPos * scale, a, arrayLocation, elementKind);
i += scale;
}
}
}
}
private static final SnippetCounter.Group checkCounters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy checkInputs") : null;
private static final SnippetCounter checkSuccessCounter = new SnippetCounter(checkCounters, "checkSuccess", "checkSuccess");
private static final SnippetCounter checkAIOOBECounter = new SnippetCounter(checkCounters, "checkAIOOBE", "checkAIOOBE");
private static final SnippetCounter.Group counters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy") : null;
private static final SnippetCounter objectCheckcastCounter = new SnippetCounter(counters, "Object[]{non-exact}", "arraycopy for non-exact Object[] arrays");
private static final SnippetCounter objectCheckcastSameTypeCounter = new SnippetCounter(counters, "Object[]{same-type}", "arraycopy call for src.klass == dest.klass Object[] arrays");
private static final SnippetCounter predictedObjectArrayCopySlowPathCounter = new SnippetCounter(counters, "Object[]{slow-path}", "used System.arraycopy slow path for predicted Object[] arrays");
private static final SnippetCounter predictedObjectArrayCopyFastPathCounter = new SnippetCounter(counters, "Object[]{fast-path}", "used oop_arraycopy for predicted Object[] arrays");
private static final EnumMap<JavaKind, SnippetCounter> arraycopyCallCounters = new EnumMap<>(JavaKind.class);
private static final EnumMap<JavaKind, SnippetCounter> arraycopyCounters = new EnumMap<>(JavaKind.class);
private static final EnumMap<JavaKind, SnippetCounter> arraycopyCallCopiedCounters = new EnumMap<>(JavaKind.class);
private static final EnumMap<JavaKind, SnippetCounter> arraycopyCopiedCounters = new EnumMap<>(JavaKind.class);
static void createArraycopyCounter(JavaKind kind) {
arraycopyCallCounters.put(kind, new SnippetCounter(counters, kind + "[]{stub}", "arraycopy call for " + kind + "[] arrays"));
arraycopyCounters.put(kind, new SnippetCounter(counters, kind + "[]{inline}", "inline arraycopy for " + kind + "[] arrays"));
arraycopyCallCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[]{stub}", "arraycopy call for " + kind + "[] arrays"));
arraycopyCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[]{inline}", "inline arraycopy for " + kind + "[] arrays"));
}
static {
createArraycopyCounter(JavaKind.Byte);
createArraycopyCounter(JavaKind.Boolean);
createArraycopyCounter(JavaKind.Char);
createArraycopyCounter(JavaKind.Short);
createArraycopyCounter(JavaKind.Int);
createArraycopyCounter(JavaKind.Long);
createArraycopyCounter(JavaKind.Float);
createArraycopyCounter(JavaKind.Double);
createArraycopyCounter(JavaKind.Object);
}
private static final SnippetCounter genericPrimitiveCallCounter = new SnippetCounter(counters, "genericPrimitive", "generic arraycopy snippet for primitive arrays");
private static final SnippetCounter genericObjectExactCallCounter = new SnippetCounter(counters, "genericObjectExact", "generic arraycopy snippet for special object arrays");
private static final SnippetCounter SystemArraycopyCounter = new SnippetCounter(counters, "genericObject", "call to System.arraycopy");
private static final SnippetCounter.Group lengthCounters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy 0-length checks") : null;
private static final SnippetCounter zeroLengthStaticCounter = new SnippetCounter(lengthCounters, "0-lengthcopy static", "arraycopy where the length is statically 0");
private static final SnippetCounter zeroLengthDynamicCounter = new SnippetCounter(lengthCounters, "0-lengthcopy dynamically", "arraycopy where the length is dynamically 0");
private static final SnippetCounter nonZeroLengthDynamicCounter = new SnippetCounter(lengthCounters, "non-0-lengthcopy dynamically", "arraycopy where the length is dynamically not zero");
private static final SnippetCounter.Group copiedCounters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy copied elements") : null;
private static final SnippetCounter nonZeroLengthDynamicCopiedCounter = new SnippetCounter(copiedCounters, "non-0-lengthcopy dynamically", "arraycopy where the length is dynamically not zero");
private static final SnippetCounter genericPrimitiveCallCopiedCounter = new SnippetCounter(copiedCounters, "genericPrimitive", "generic arraycopy snippet for primitive arrays");
private static final SnippetCounter genericObjectExactCallCopiedCounter = new SnippetCounter(copiedCounters, "genericObjectExact", "generic arraycopy snippet for special object arrays");
private static final SnippetCounter SystemArraycopyCopiedCounter = new SnippetCounter(copiedCounters, "genericObject", "call to System.arraycopy");
private static final SnippetCounter objectCheckcastCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{non-exact}", "arraycopy for non-exact Object[] arrays");
private static final SnippetCounter objectCheckcastSameTypeCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{same-type}", "arraycopy call for src.klass == dest.klass Object[] arrays");
private static final SnippetCounter predictedObjectArrayCopySlowPathCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{slow-path}",
"used System.arraycopy slow path for predicted Object[] arrays");
private static final SnippetCounter predictedObjectArrayCopyFastPathCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{fast-path}", "used oop_arraycopy for predicted Object[] arrays");
public static class Templates extends SnippetTemplate.AbstractTemplates {
public Templates(HotSpotProviders providers, TargetDescription target) {
super(providers, providers.getSnippetReflection(), target);
}
private ResolvedJavaMethod originalArraycopy() throws GraalError {
if (originalArraycopy == null) {
Method method;
try {
method = System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new GraalError(e);
}
originalArraycopy = providers.getMetaAccess().lookupJavaMethod(method);
}
return originalArraycopy;
}
private ResolvedJavaMethod originalArraycopy;
private final SnippetInfo checkcastArraycopyWorkSnippet = snippet("checkcastArraycopyWork");
private final SnippetInfo arraycopyGenericSnippet = snippet("arraycopyGeneric");
private final SnippetInfo arraycopySlowPathIntrinsicSnippet = snippet("arraycopySlowPathIntrinsic");
private final SnippetInfo arraycopyUnrolledIntrinsicSnippet = snippet("arraycopyUnrolledIntrinsic");
private final SnippetInfo arraycopyExactIntrinsicSnippet = snippet("arraycopyExactIntrinsic");
private final SnippetInfo arraycopyZeroLengthIntrinsicSnippet = snippet("arraycopyZeroLengthIntrinsic");
private final SnippetInfo arraycopyPredictedExactIntrinsicSnippet = snippet("arraycopyPredictedExactIntrinsic");
private final SnippetInfo arraycopyPredictedObjectWorkSnippet = snippet("arraycopyPredictedObjectWork");
private final SnippetInfo arraycopyUnrolledWorkSnippet = snippet("arraycopyUnrolledWork");
protected SnippetInfo snippet(String methodName) {
SnippetInfo info = snippet(ArrayCopySnippets.class, methodName, LocationIdentity.any());
info.setOriginalMethod(originalArraycopy());
return info;
}
public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy) {
return selectComponentKind(arraycopy, true);
}
public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy, boolean exact) {
ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp());
ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp());
if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) {
if (!exact) {
JavaKind component = getComponentKind(srcType);
if (component != null) {
return component;
}
return getComponentKind(destType);
}
return null;
}
if (exact) {
if (!destType.getComponentType().isAssignableFrom(srcType.getComponentType())) {
return null;
}
if (!arraycopy.isExact()) {
return null;
}
}
return srcType.getComponentType().getJavaKind();
}
private static JavaKind getComponentKind(ResolvedJavaType type) {
if (type != null && type.isArray()) {
return type.getComponentType().getJavaKind();
}
return null;
}
private static boolean shouldUnroll(ValueNode length) {
return length.isConstant() && length.asJavaConstant().asInt() <= 8 && length.asJavaConstant().asInt() != 0;
}
public void lower(ArrayCopyNode arraycopy, LoweringTool tool) {
JavaKind componentKind = selectComponentKind(arraycopy);
SnippetInfo snippetInfo = null;
SnippetInfo slowPathSnippetInfo = null;
Object slowPathArgument = null;
if (arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) {
snippetInfo = arraycopyZeroLengthIntrinsicSnippet;
} else if (arraycopy.isExact()) {
snippetInfo = arraycopyExactIntrinsicSnippet;
if (shouldUnroll(arraycopy.getLength())) {
snippetInfo = arraycopyUnrolledIntrinsicSnippet;
}
} else {
if (componentKind == JavaKind.Object) {
ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp());
ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp());
ResolvedJavaType srcComponentType = srcType == null ? null : srcType.getComponentType();
ResolvedJavaType destComponentType = destType == null ? null : destType.getComponentType();
if (srcComponentType != null && destComponentType != null && !srcComponentType.isPrimitive() && !destComponentType.isPrimitive()) {
snippetInfo = arraycopySlowPathIntrinsicSnippet;
slowPathSnippetInfo = checkcastArraycopyWorkSnippet;
slowPathArgument = LocationIdentity.any();
/*
* Because this snippet has to use Sysytem.arraycopy as a slow path, we must
* pretend to kill any() so clear the componentKind.
*/
componentKind = null;
}
}
if (componentKind == null && snippetInfo == null) {
JavaKind predictedKind = selectComponentKind(arraycopy, false);
if (predictedKind != null) {
/*
* At least one array is of a known type requiring no store checks, so
* assume the other is of the same type. Generally this is working around
* deficiencies in our propagation of type information.
*/
componentKind = predictedKind;
if (predictedKind == JavaKind.Object) {
snippetInfo = arraycopySlowPathIntrinsicSnippet;
slowPathSnippetInfo = arraycopyPredictedObjectWorkSnippet;
slowPathArgument = predictedKind;
componentKind = null;
} else {
snippetInfo = arraycopyPredictedExactIntrinsicSnippet;
}
}
}
if (snippetInfo == null) {
snippetInfo = arraycopyGenericSnippet;
}
}
Arguments args = new Arguments(snippetInfo, arraycopy.graph().getGuardsStage(), tool.getLoweringStage());
args.add("src", arraycopy.getSource());
args.add("srcPos", arraycopy.getSourcePosition());
args.add("dest", arraycopy.getDestination());
args.add("destPos", arraycopy.getDestinationPosition());
args.add("length", arraycopy.getLength());
if (snippetInfo == arraycopyUnrolledIntrinsicSnippet) {
args.addConst("unrolledLength", arraycopy.getLength().asJavaConstant().asInt());
args.addConst("elementKind", componentKind != null ? componentKind : JavaKind.Illegal);
} else if (snippetInfo == arraycopySlowPathIntrinsicSnippet) {
ValueNode predictedKlass = null;
if (slowPathArgument == arraycopyPredictedObjectWorkSnippet) {
HotSpotResolvedObjectType arrayClass = (HotSpotResolvedObjectType) tool.getMetaAccess().lookupJavaType(Object[].class);
predictedKlass = ConstantNode.forConstant(KlassPointerStamp.klassNonNull(), arrayClass.klass(), tool.getMetaAccess(), arraycopy.graph());
} else {
predictedKlass = ConstantNode.forConstant(KlassPointerStamp.klassAlwaysNull(), JavaConstant.NULL_POINTER, tool.getMetaAccess(), arraycopy.graph());
}
args.add("predictedKlass", predictedKlass);
args.addConst("elementKind", componentKind != null ? componentKind : JavaKind.Illegal);
args.addConst("slowPath", slowPathSnippetInfo);
assert slowPathArgument != null;
args.addConst("slowPathArgument", slowPathArgument);
} else if (snippetInfo == arraycopyExactIntrinsicSnippet || snippetInfo == arraycopyPredictedExactIntrinsicSnippet) {
assert componentKind != null;
args.addConst("elementKind", componentKind);
args.addConst("counter", arraycopyCallCounters.get(componentKind));
args.addConst("copiedCounter", arraycopyCallCopiedCounters.get(componentKind));
}
instantiate(args, arraycopy);
}
public void lower(ArrayCopySlowPathNode arraycopy, LoweringTool tool) {
StructuredGraph graph = arraycopy.graph();
if (!graph.getGuardsStage().areFrameStatesAtDeopts()) {
// Can't be lowered yet
return;
}
SnippetInfo snippetInfo = arraycopy.getSnippet();
Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage());
args.add("nonNullSrc", arraycopy.getSource());
args.add("srcPos", arraycopy.getSourcePosition());
args.add("nonNullDest", arraycopy.getDestination());
args.add("destPos", arraycopy.getDestinationPosition());
if (snippetInfo == arraycopyUnrolledWorkSnippet) {
args.addConst("length", ((Integer) arraycopy.getArgument()).intValue());
args.addConst("elementKind", arraycopy.getElementKind());
} else {
args.add("length", arraycopy.getLength());
}
if (snippetInfo == arraycopyPredictedObjectWorkSnippet) {
args.add("objectArrayKlass", arraycopy.getPredictedKlass());
args.addConst("counter", arraycopyCallCounters.get(JavaKind.Object));
args.addConst("copiedCounter", arraycopyCallCopiedCounters.get(JavaKind.Object));
}
instantiate(args, arraycopy);
}
public void lower(ArrayCopyUnrollNode arraycopy, LoweringTool tool) {
StructuredGraph graph = arraycopy.graph();
if (!graph.getGuardsStage().areFrameStatesAtDeopts()) {
// Can't be lowered yet
return;
}
SnippetInfo snippetInfo = arraycopyUnrolledWorkSnippet;
Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage());
args.add("nonNullSrc", arraycopy.getSource());
args.add("srcPos", arraycopy.getSourcePosition());
args.add("nonNullDest", arraycopy.getDestination());
args.add("destPos", arraycopy.getDestinationPosition());
args.addConst("length", arraycopy.getUnrollLength());
args.addConst("elementKind", arraycopy.getElementKind());
template(args).instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
}
/**
* Instantiate the snippet template and fix up the FrameState of any Invokes of
* System.arraycopy and propagate the captured bci in the ArrayCopySlowPathNode.
*
* @param args
* @param arraycopy
*/
private void instantiate(Arguments args, BasicArrayCopyNode arraycopy) {
StructuredGraph graph = arraycopy.graph();
SnippetTemplate template = template(args);
Map<Node, Node> replacements = template.instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
for (Node originalNode : replacements.keySet()) {
if (originalNode instanceof Invoke) {
Invoke invoke = (Invoke) replacements.get(originalNode);
assert invoke.asNode().graph() == graph;
CallTargetNode call = invoke.callTarget();
if (!call.targetMethod().equals(originalArraycopy)) {
throw new GraalError("unexpected invoke %s in snippet", call.targetMethod());
}
// Here we need to fix the bci of the invoke
InvokeNode newInvoke = graph.add(new InvokeNode(invoke.callTarget(), arraycopy.getBci()));
if (arraycopy.stateDuring() != null) {
newInvoke.setStateDuring(arraycopy.stateDuring());
} else {
assert arraycopy.stateAfter() != null;
newInvoke.setStateAfter(arraycopy.stateAfter());
}
graph.replaceFixedWithFixed((InvokeNode) invoke.asNode(), newInvoke);
} else if (originalNode instanceof ArrayCopySlowPathNode) {
ArrayCopySlowPathNode slowPath = (ArrayCopySlowPathNode) replacements.get(originalNode);
assert arraycopy.stateAfter() != null;
slowPath.setStateAfter(arraycopy.stateAfter());
slowPath.setBci(arraycopy.getBci());
}
}
}
}
}