001/*
002 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot.replacements.arraycopy;
024
025import static com.oracle.graal.compiler.common.GraalOptions.*;
026import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
027import static com.oracle.graal.nodes.extended.BranchProbabilityNode.*;
028
029import java.lang.reflect.*;
030import java.util.*;
031
032import jdk.internal.jvmci.code.*;
033import jdk.internal.jvmci.common.*;
034import jdk.internal.jvmci.hotspot.*;
035import jdk.internal.jvmci.meta.*;
036
037import com.oracle.graal.api.directives.*;
038import com.oracle.graal.api.replacements.*;
039import com.oracle.graal.graph.*;
040import com.oracle.graal.hotspot.meta.*;
041import com.oracle.graal.hotspot.word.*;
042import com.oracle.graal.nodes.*;
043import com.oracle.graal.nodes.extended.*;
044import com.oracle.graal.nodes.java.*;
045import com.oracle.graal.nodes.spi.*;
046import com.oracle.graal.nodes.type.*;
047import com.oracle.graal.replacements.*;
048import com.oracle.graal.replacements.Snippet.ConstantParameter;
049import com.oracle.graal.replacements.SnippetTemplate.Arguments;
050import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo;
051import com.oracle.graal.replacements.nodes.*;
052import com.oracle.graal.word.*;
053
054public class ArrayCopySnippets implements Snippets {
055
056    private static int checkArrayType(KlassPointer hub) {
057        int layoutHelper = readLayoutHelper(hub);
058        if (probability(SLOW_PATH_PROBABILITY, layoutHelper >= 0)) {
059            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
060        }
061        return layoutHelper;
062    }
063
064    private static void checkLimits(Object src, int srcPos, Object dest, int destPos, int length) {
065        if (probability(SLOW_PATH_PROBABILITY, srcPos < 0)) {
066            checkAIOOBECounter.inc();
067            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
068        }
069        if (probability(SLOW_PATH_PROBABILITY, destPos < 0)) {
070            checkAIOOBECounter.inc();
071            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
072        }
073        if (probability(SLOW_PATH_PROBABILITY, length < 0)) {
074            checkAIOOBECounter.inc();
075            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
076        }
077        if (probability(SLOW_PATH_PROBABILITY, srcPos + length > ArrayLengthNode.arrayLength(src))) {
078            checkAIOOBECounter.inc();
079            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
080        }
081        if (probability(SLOW_PATH_PROBABILITY, destPos + length > ArrayLengthNode.arrayLength(dest))) {
082            checkAIOOBECounter.inc();
083            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
084        }
085        checkSuccessCounter.inc();
086    }
087
088    @Snippet
089    public static void arraycopyZeroLengthIntrinsic(Object src, int srcPos, Object dest, int destPos, int length) {
090        Object nonNullSrc = GraalDirectives.guardingNonNull(src);
091        Object nonNullDest = GraalDirectives.guardingNonNull(dest);
092        KlassPointer srcHub = loadHub(nonNullSrc);
093        KlassPointer destHub = loadHub(nonNullDest);
094        checkArrayType(srcHub);
095        checkArrayType(destHub);
096        checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
097        zeroLengthStaticCounter.inc();
098    }
099
100    @Snippet
101    public static void arraycopyExactIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Kind elementKind, @ConstantParameter SnippetCounter counter,
102                    @ConstantParameter SnippetCounter copiedCounter) {
103        Object nonNullSrc = GraalDirectives.guardingNonNull(src);
104        Object nonNullDest = GraalDirectives.guardingNonNull(dest);
105        checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
106        counter.inc();
107        copiedCounter.add(length);
108        ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind);
109        if (length == 0) {
110            zeroLengthDynamicCounter.inc();
111        } else {
112            nonZeroLengthDynamicCounter.inc();
113            nonZeroLengthDynamicCopiedCounter.add(length);
114        }
115    }
116
117    /**
118     * This intrinsic is useful for the case where we know something statically about one of the
119     * inputs but not the other.
120     */
121    @Snippet
122    public static void arraycopyPredictedExactIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Kind elementKind, @ConstantParameter SnippetCounter counter,
123                    @ConstantParameter SnippetCounter copiedCounter) {
124        Object nonNullSrc = GraalDirectives.guardingNonNull(src);
125        Object nonNullDest = GraalDirectives.guardingNonNull(dest);
126        KlassPointer srcHub = loadHub(nonNullSrc);
127        KlassPointer destHub = loadHub(nonNullDest);
128        if (probability(SLOW_PATH_PROBABILITY, srcHub != destHub)) {
129            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
130        }
131        checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
132        counter.inc();
133        copiedCounter.add(length);
134        ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind);
135        if (length == 0) {
136            zeroLengthDynamicCounter.inc();
137        } else {
138            nonZeroLengthDynamicCounter.inc();
139            nonZeroLengthDynamicCopiedCounter.add(length);
140        }
141    }
142
143    @Snippet
144    public static void arraycopyPredictedObjectWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length, KlassPointer objectArrayKlass,
145                    @ConstantParameter SnippetCounter counter, @ConstantParameter SnippetCounter copiedCounter) {
146        if (length > 0) {
147            KlassPointer srcHub = loadHub(nonNullSrc);
148            KlassPointer destHub = loadHub(nonNullDest);
149            if (probability(FAST_PATH_PROBABILITY, srcHub == destHub || destHub == objectArrayKlass)) {
150                counter.inc();
151                copiedCounter.add(length);
152                predictedObjectArrayCopyFastPathCounter.inc();
153                predictedObjectArrayCopyFastPathCopiedCounter.add(length);
154                ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length);
155            } else {
156                predictedObjectArrayCopySlowPathCounter.inc();
157                predictedObjectArrayCopySlowPathCopiedCounter.add(length);
158                System.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length);
159            }
160        }
161    }
162
163    /**
164     * This is the basic template for the full arraycopy checks, including a check that the
165     * underlying type is really an array type.
166     */
167    @Snippet
168    public static void arraycopySlowPathIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Kind elementKind, @ConstantParameter SnippetInfo slowPath,
169                    @ConstantParameter Object slowPathArgument) {
170        Object nonNullSrc = GraalDirectives.guardingNonNull(src);
171        Object nonNullDest = GraalDirectives.guardingNonNull(dest);
172        KlassPointer srcHub = loadHub(nonNullSrc);
173        KlassPointer destHub = loadHub(nonNullDest);
174        checkArrayType(srcHub);
175        checkArrayType(destHub);
176        checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
177        if (length == 0) {
178            zeroLengthDynamicCounter.inc();
179        } else {
180            nonZeroLengthDynamicCounter.inc();
181            nonZeroLengthDynamicCopiedCounter.add(length);
182        }
183        ArrayCopySlowPathNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, elementKind, slowPath, slowPathArgument);
184    }
185
186    /**
187     * Snippet for unrolled arraycopy.
188     */
189    @Snippet
190    public static void arraycopyUnrolledIntrinsic(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter int unrolledLength, @ConstantParameter Kind elementKind) {
191        Object nonNullSrc = GraalDirectives.guardingNonNull(src);
192        Object nonNullDest = GraalDirectives.guardingNonNull(dest);
193        checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
194        if (length == 0) {
195            zeroLengthDynamicCounter.inc();
196        } else {
197            nonZeroLengthDynamicCounter.inc();
198            nonZeroLengthDynamicCopiedCounter.add(length);
199        }
200        ArrayCopyUnrollNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, unrolledLength, elementKind);
201    }
202
203    @Snippet
204    public static void checkcastArraycopyWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, int length) {
205        if (length > 0) {
206            KlassPointer destKlass = loadHub(nonNullDest);
207            KlassPointer srcKlass = loadHub(nonNullSrc);
208            if (probability(SLOW_PATH_PROBABILITY, srcKlass == destKlass)) {
209                // no storecheck required.
210                objectCheckcastSameTypeCounter.inc();
211                objectCheckcastSameTypeCopiedCounter.add(length);
212                ArrayCopyCallNode.arraycopyObjectKillsAny(nonNullSrc, srcPos, nonNullDest, destPos, length);
213            } else {
214                KlassPointer destElemKlass = destKlass.readKlassPointer(arrayClassElementOffset(), OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION);
215                Word superCheckOffset = Word.signed(destElemKlass.readInt(superCheckOffsetOffset(), KLASS_SUPER_CHECK_OFFSET_LOCATION));
216                objectCheckcastCounter.inc();
217                objectCheckcastCopiedCounter.add(length);
218                int copiedElements = CheckcastArrayCopyCallNode.checkcastArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, superCheckOffset, destElemKlass, false);
219                if (copiedElements != 0) {
220                    /*
221                     * the checkcast stub doesn't throw the ArrayStoreException, but returns the
222                     * number of copied elements (xor'd with -1).
223                     */
224                    copiedElements ^= -1;
225                    System.arraycopy(nonNullSrc, srcPos + copiedElements, nonNullDest, destPos + copiedElements, length - copiedElements);
226                }
227            }
228        }
229    }
230
231    @Snippet
232    public static void arraycopyGeneric(Object src, int srcPos, Object dest, int destPos, int length) {
233        Object nonNullSrc = GraalDirectives.guardingNonNull(src);
234        Object nonNullDest = GraalDirectives.guardingNonNull(dest);
235        KlassPointer srcHub = loadHub(nonNullSrc);
236        KlassPointer destHub = loadHub(nonNullDest);
237        if (probability(FAST_PATH_PROBABILITY, srcHub.equal(destHub)) && probability(FAST_PATH_PROBABILITY, nonNullSrc != nonNullDest)) {
238            int layoutHelper = checkArrayType(srcHub);
239            final boolean isObjectArray = ((layoutHelper & layoutHelperElementTypePrimitiveInPlace()) == 0);
240            checkLimits(nonNullSrc, srcPos, nonNullDest, destPos, length);
241            if (probability(FAST_PATH_PROBABILITY, isObjectArray)) {
242                genericObjectExactCallCounter.inc();
243                genericObjectExactCallCopiedCounter.add(length);
244                ArrayCopyCallNode.disjointArraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, Kind.Object);
245            } else {
246                genericPrimitiveCallCounter.inc();
247                genericPrimitiveCallCopiedCounter.add(length);
248                UnsafeArrayCopyNode.arraycopyPrimitive(nonNullSrc, srcPos, nonNullDest, destPos, length, layoutHelper);
249            }
250        } else {
251            SystemArraycopyCounter.inc();
252            SystemArraycopyCopiedCounter.add(length);
253            System.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length);
254        }
255    }
256
257    @Fold
258    private static LocationIdentity getArrayLocation(Kind kind) {
259        return NamedLocationIdentity.getArrayLocation(kind);
260    }
261
262    @Snippet
263    public static void arraycopyUnrolledWork(Object nonNullSrc, int srcPos, Object nonNullDest, int destPos, @ConstantParameter int length, @ConstantParameter Kind elementKind) {
264        final int scale = arrayIndexScale(elementKind);
265        int arrayBaseOffset = arrayBaseOffset(elementKind);
266        LocationIdentity arrayLocation = getArrayLocation(elementKind);
267        if (nonNullSrc == nonNullDest && srcPos < destPos) { // bad aliased case
268            long start = (long) (length - 1) * scale;
269            long i = start;
270            ExplodeLoopNode.explodeLoop();
271            for (int iteration = 0; iteration < length; iteration++) {
272                if (i >= 0) {
273                    Object a = UnsafeLoadNode.load(nonNullSrc, arrayBaseOffset + i + (long) srcPos * scale, elementKind, arrayLocation);
274                    DirectObjectStoreNode.storeObject(nonNullDest, arrayBaseOffset, i + (long) destPos * scale, a, arrayLocation, elementKind);
275                    i -= scale;
276                }
277            }
278        } else {
279            long end = (long) length * scale;
280            long i = 0;
281            ExplodeLoopNode.explodeLoop();
282            for (int iteration = 0; iteration < length; iteration++) {
283                if (i < end) {
284                    Object a = UnsafeLoadNode.load(nonNullSrc, arrayBaseOffset + i + (long) srcPos * scale, elementKind, arrayLocation);
285                    DirectObjectStoreNode.storeObject(nonNullDest, arrayBaseOffset, i + (long) destPos * scale, a, arrayLocation, elementKind);
286                    i += scale;
287                }
288            }
289        }
290    }
291
292    private static final SnippetCounter.Group checkCounters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy checkInputs") : null;
293    private static final SnippetCounter checkSuccessCounter = new SnippetCounter(checkCounters, "checkSuccess", "checkSuccess");
294    private static final SnippetCounter checkAIOOBECounter = new SnippetCounter(checkCounters, "checkAIOOBE", "checkAIOOBE");
295
296    private static final SnippetCounter.Group counters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy") : null;
297
298    private static final SnippetCounter objectCheckcastCounter = new SnippetCounter(counters, "Object[]{non-exact}", "arraycopy for non-exact Object[] arrays");
299    private static final SnippetCounter objectCheckcastSameTypeCounter = new SnippetCounter(counters, "Object[]{same-type}", "arraycopy call for src.klass == dest.klass Object[] arrays");
300    private static final SnippetCounter predictedObjectArrayCopySlowPathCounter = new SnippetCounter(counters, "Object[]{slow-path}", "used System.arraycopy slow path for predicted Object[] arrays");
301    private static final SnippetCounter predictedObjectArrayCopyFastPathCounter = new SnippetCounter(counters, "Object[]{fast-path}", "used oop_arraycopy for predicted Object[] arrays");
302
303    private static final EnumMap<Kind, SnippetCounter> arraycopyCallCounters = new EnumMap<>(Kind.class);
304    private static final EnumMap<Kind, SnippetCounter> arraycopyCounters = new EnumMap<>(Kind.class);
305
306    private static final EnumMap<Kind, SnippetCounter> arraycopyCallCopiedCounters = new EnumMap<>(Kind.class);
307    private static final EnumMap<Kind, SnippetCounter> arraycopyCopiedCounters = new EnumMap<>(Kind.class);
308
309    static void createArraycopyCounter(Kind kind) {
310        arraycopyCallCounters.put(kind, new SnippetCounter(counters, kind + "[]{stub}", "arraycopy call for " + kind + "[] arrays"));
311        arraycopyCounters.put(kind, new SnippetCounter(counters, kind + "[]{inline}", "inline arraycopy for " + kind + "[] arrays"));
312
313        arraycopyCallCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[]{stub}", "arraycopy call for " + kind + "[] arrays"));
314        arraycopyCopiedCounters.put(kind, new SnippetCounter(copiedCounters, kind + "[]{inline}", "inline arraycopy for " + kind + "[] arrays"));
315    }
316
317    static {
318        createArraycopyCounter(Kind.Byte);
319        createArraycopyCounter(Kind.Boolean);
320        createArraycopyCounter(Kind.Char);
321        createArraycopyCounter(Kind.Short);
322        createArraycopyCounter(Kind.Int);
323        createArraycopyCounter(Kind.Long);
324        createArraycopyCounter(Kind.Float);
325        createArraycopyCounter(Kind.Double);
326        createArraycopyCounter(Kind.Object);
327    }
328
329    private static final SnippetCounter genericPrimitiveCallCounter = new SnippetCounter(counters, "genericPrimitive", "generic arraycopy snippet for primitive arrays");
330    private static final SnippetCounter genericObjectExactCallCounter = new SnippetCounter(counters, "genericObjectExact", "generic arraycopy snippet for special object arrays");
331    private static final SnippetCounter SystemArraycopyCounter = new SnippetCounter(counters, "genericObject", "call to System.arraycopy");
332
333    private static final SnippetCounter.Group lengthCounters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy 0-length checks") : null;
334
335    private static final SnippetCounter zeroLengthStaticCounter = new SnippetCounter(lengthCounters, "0-lengthcopy static", "arraycopy where the length is statically 0");
336    private static final SnippetCounter zeroLengthDynamicCounter = new SnippetCounter(lengthCounters, "0-lengthcopy dynamically", "arraycopy where the length is dynamically 0");
337    private static final SnippetCounter nonZeroLengthDynamicCounter = new SnippetCounter(lengthCounters, "non-0-lengthcopy dynamically", "arraycopy where the length is dynamically not zero");
338
339    private static final SnippetCounter.Group copiedCounters = SnippetCounters.getValue() ? new SnippetCounter.Group("System.arraycopy copied elements") : null;
340
341    private static final SnippetCounter nonZeroLengthDynamicCopiedCounter = new SnippetCounter(copiedCounters, "non-0-lengthcopy dynamically", "arraycopy where the length is dynamically not zero");
342    private static final SnippetCounter genericPrimitiveCallCopiedCounter = new SnippetCounter(copiedCounters, "genericPrimitive", "generic arraycopy snippet for primitive arrays");
343    private static final SnippetCounter genericObjectExactCallCopiedCounter = new SnippetCounter(copiedCounters, "genericObjectExact", "generic arraycopy snippet for special object arrays");
344    private static final SnippetCounter SystemArraycopyCopiedCounter = new SnippetCounter(copiedCounters, "genericObject", "call to System.arraycopy");
345
346    private static final SnippetCounter objectCheckcastCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{non-exact}", "arraycopy for non-exact Object[] arrays");
347    private static final SnippetCounter objectCheckcastSameTypeCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{same-type}", "arraycopy call for src.klass == dest.klass Object[] arrays");
348    private static final SnippetCounter predictedObjectArrayCopySlowPathCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{slow-path}",
349                    "used System.arraycopy slow path for predicted Object[] arrays");
350    private static final SnippetCounter predictedObjectArrayCopyFastPathCopiedCounter = new SnippetCounter(copiedCounters, "Object[]{fast-path}", "used oop_arraycopy for predicted Object[] arrays");
351
352    public static class Templates extends SnippetTemplate.AbstractTemplates {
353
354        public Templates(HotSpotProviders providers, TargetDescription target) {
355            super(providers, providers.getSnippetReflection(), target);
356        }
357
358        private ResolvedJavaMethod originalArraycopy() throws JVMCIError {
359            if (originalArraycopy == null) {
360                Method method;
361                try {
362                    method = System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class);
363                } catch (NoSuchMethodException | SecurityException e) {
364                    throw new JVMCIError(e);
365                }
366                originalArraycopy = providers.getMetaAccess().lookupJavaMethod(method);
367            }
368            return originalArraycopy;
369        }
370
371        private ResolvedJavaMethod originalArraycopy;
372
373        private final SnippetInfo checkcastArraycopyWorkSnippet = snippet("checkcastArraycopyWork");
374        private final SnippetInfo arraycopyGenericSnippet = snippet("arraycopyGeneric");
375
376        private final SnippetInfo arraycopySlowPathIntrinsicSnippet = snippet("arraycopySlowPathIntrinsic");
377        private final SnippetInfo arraycopyUnrolledIntrinsicSnippet = snippet("arraycopyUnrolledIntrinsic");
378        private final SnippetInfo arraycopyExactIntrinsicSnippet = snippet("arraycopyExactIntrinsic");
379        private final SnippetInfo arraycopyZeroLengthIntrinsicSnippet = snippet("arraycopyZeroLengthIntrinsic");
380        private final SnippetInfo arraycopyPredictedExactIntrinsicSnippet = snippet("arraycopyPredictedExactIntrinsic");
381        private final SnippetInfo arraycopyPredictedObjectWorkSnippet = snippet("arraycopyPredictedObjectWork");
382
383        private final SnippetInfo arraycopyUnrolledWorkSnippet = snippet("arraycopyUnrolledWork");
384
385        protected SnippetInfo snippet(String methodName) {
386            SnippetInfo info = snippet(ArrayCopySnippets.class, methodName, LocationIdentity.any());
387            info.setOriginalMethod(originalArraycopy());
388            return info;
389        }
390
391        public static Kind selectComponentKind(BasicArrayCopyNode arraycopy) {
392            return selectComponentKind(arraycopy, true);
393        }
394
395        public static Kind selectComponentKind(BasicArrayCopyNode arraycopy, boolean exact) {
396            ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp());
397            ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp());
398
399            if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) {
400                if (!exact) {
401                    Kind component = getComponentKind(srcType);
402                    if (component != null) {
403                        return component;
404                    }
405                    return getComponentKind(destType);
406                }
407                return null;
408            }
409            if (exact) {
410                if (!destType.getComponentType().isAssignableFrom(srcType.getComponentType())) {
411                    return null;
412                }
413                if (!arraycopy.isExact()) {
414                    return null;
415                }
416            }
417            return srcType.getComponentType().getKind();
418        }
419
420        private static Kind getComponentKind(ResolvedJavaType type) {
421            if (type != null && type.isArray()) {
422                return type.getComponentType().getKind();
423            }
424            return null;
425        }
426
427        private static boolean shouldUnroll(ValueNode length) {
428            return length.isConstant() && length.asJavaConstant().asInt() <= 8 && length.asJavaConstant().asInt() != 0;
429        }
430
431        public void lower(ArrayCopyNode arraycopy, LoweringTool tool) {
432            Kind componentKind = selectComponentKind(arraycopy);
433            SnippetInfo snippetInfo = null;
434            SnippetInfo slowPathSnippetInfo = null;
435            Object slowPathArgument = null;
436
437            if (arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) {
438                snippetInfo = arraycopyZeroLengthIntrinsicSnippet;
439            } else if (arraycopy.isExact()) {
440                snippetInfo = arraycopyExactIntrinsicSnippet;
441                if (shouldUnroll(arraycopy.getLength())) {
442                    snippetInfo = arraycopyUnrolledIntrinsicSnippet;
443                }
444            } else {
445                if (componentKind == Kind.Object) {
446                    ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp());
447                    ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp());
448                    ResolvedJavaType srcComponentType = srcType == null ? null : srcType.getComponentType();
449                    ResolvedJavaType destComponentType = destType == null ? null : destType.getComponentType();
450                    if (srcComponentType != null && destComponentType != null && !srcComponentType.isPrimitive() && !destComponentType.isPrimitive()) {
451                        snippetInfo = arraycopySlowPathIntrinsicSnippet;
452                        slowPathSnippetInfo = checkcastArraycopyWorkSnippet;
453                        slowPathArgument = LocationIdentity.any();
454                        /*
455                         * Because this snippet has to use Sysytem.arraycopy as a slow path, we must
456                         * pretend to kill any() so clear the componentKind.
457                         */
458                        componentKind = null;
459                    }
460                }
461                if (componentKind == null && snippetInfo == null) {
462                    Kind predictedKind = selectComponentKind(arraycopy, false);
463                    if (predictedKind != null) {
464                        /*
465                         * At least one array is of a known type requiring no store checks, so
466                         * assume the other is of the same type. Generally this is working around
467                         * deficiencies in our propagation of type information.
468                         */
469                        componentKind = predictedKind;
470                        if (predictedKind == Kind.Object) {
471                            snippetInfo = arraycopySlowPathIntrinsicSnippet;
472                            slowPathSnippetInfo = arraycopyPredictedObjectWorkSnippet;
473                            slowPathArgument = predictedKind;
474                            componentKind = null;
475                        } else {
476                            snippetInfo = arraycopyPredictedExactIntrinsicSnippet;
477                        }
478                    }
479                }
480                if (snippetInfo == null) {
481                    snippetInfo = arraycopyGenericSnippet;
482                }
483            }
484            Arguments args = new Arguments(snippetInfo, arraycopy.graph().getGuardsStage(), tool.getLoweringStage());
485            args.add("src", arraycopy.getSource());
486            args.add("srcPos", arraycopy.getSourcePosition());
487            args.add("dest", arraycopy.getDestination());
488            args.add("destPos", arraycopy.getDestinationPosition());
489            args.add("length", arraycopy.getLength());
490            if (snippetInfo == arraycopyUnrolledIntrinsicSnippet) {
491                args.addConst("unrolledLength", arraycopy.getLength().asJavaConstant().asInt());
492                args.addConst("elementKind", componentKind != null ? componentKind : Kind.Illegal);
493            } else if (snippetInfo == arraycopySlowPathIntrinsicSnippet) {
494                args.addConst("elementKind", componentKind != null ? componentKind : Kind.Illegal);
495                args.addConst("slowPath", slowPathSnippetInfo);
496                assert slowPathArgument != null;
497                args.addConst("slowPathArgument", slowPathArgument);
498            } else if (snippetInfo == arraycopyExactIntrinsicSnippet || snippetInfo == arraycopyPredictedExactIntrinsicSnippet) {
499                assert componentKind != null;
500                args.addConst("elementKind", componentKind);
501                args.addConst("counter", arraycopyCallCounters.get(componentKind));
502                args.addConst("copiedCounter", arraycopyCallCopiedCounters.get(componentKind));
503            }
504            instantiate(args, arraycopy);
505        }
506
507        public void lower(ArrayCopySlowPathNode arraycopy, LoweringTool tool) {
508            StructuredGraph graph = arraycopy.graph();
509            if (!graph.getGuardsStage().areFrameStatesAtDeopts()) {
510                // Can't be lowered yet
511                return;
512            }
513            SnippetInfo snippetInfo = arraycopy.getSnippet();
514            Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage());
515            args.add("nonNullSrc", arraycopy.getSource());
516            args.add("srcPos", arraycopy.getSourcePosition());
517            args.add("nonNullDest", arraycopy.getDestination());
518            args.add("destPos", arraycopy.getDestinationPosition());
519            if (snippetInfo == arraycopyUnrolledWorkSnippet) {
520                args.addConst("length", ((Integer) arraycopy.getArgument()).intValue());
521                args.addConst("elementKind", arraycopy.getElementKind());
522            } else {
523                args.add("length", arraycopy.getLength());
524            }
525            if (snippetInfo == arraycopyPredictedObjectWorkSnippet) {
526                HotSpotResolvedObjectType arrayKlass = (HotSpotResolvedObjectType) tool.getMetaAccess().lookupJavaType(Object[].class);
527                ValueNode objectArrayKlass = ConstantNode.forConstant(tool.getStampProvider().createHubStamp(true), arrayKlass.klass(), tool.getMetaAccess(), arraycopy.graph());
528                args.add("objectArrayKlass", objectArrayKlass);
529                args.addConst("counter", arraycopyCallCounters.get(Kind.Object));
530                args.addConst("copiedCounter", arraycopyCallCopiedCounters.get(Kind.Object));
531            }
532            instantiate(args, arraycopy);
533        }
534
535        public void lower(ArrayCopyUnrollNode arraycopy, LoweringTool tool) {
536            StructuredGraph graph = arraycopy.graph();
537            if (!graph.getGuardsStage().areFrameStatesAtDeopts()) {
538                // Can't be lowered yet
539                return;
540            }
541            SnippetInfo snippetInfo = arraycopyUnrolledWorkSnippet;
542            Arguments args = new Arguments(snippetInfo, graph.getGuardsStage(), tool.getLoweringStage());
543            args.add("nonNullSrc", arraycopy.getSource());
544            args.add("srcPos", arraycopy.getSourcePosition());
545            args.add("nonNullDest", arraycopy.getDestination());
546            args.add("destPos", arraycopy.getDestinationPosition());
547            args.addConst("length", arraycopy.getUnrollLength());
548            args.addConst("elementKind", arraycopy.getElementKind());
549            template(args).instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
550        }
551
552        /**
553         * Instantiate the snippet template and fix up the FrameState of any Invokes of
554         * System.arraycopy and propagate the captured bci in the ArrayCopySlowPathNode.
555         *
556         * @param args
557         * @param arraycopy
558         */
559        private void instantiate(Arguments args, BasicArrayCopyNode arraycopy) {
560            StructuredGraph graph = arraycopy.graph();
561            SnippetTemplate template = template(args);
562            Map<Node, Node> replacements = template.instantiate(providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
563            for (Node originalNode : replacements.keySet()) {
564                if (originalNode instanceof Invoke) {
565                    Invoke invoke = (Invoke) replacements.get(originalNode);
566                    assert invoke.asNode().graph() == graph;
567                    CallTargetNode call = invoke.callTarget();
568
569                    if (!call.targetMethod().equals(originalArraycopy)) {
570                        throw new JVMCIError("unexpected invoke %s in snippet", call.targetMethod());
571                    }
572                    // Here we need to fix the bci of the invoke
573                    InvokeNode newInvoke = graph.add(new InvokeNode(invoke.callTarget(), arraycopy.getBci()));
574                    if (arraycopy.stateDuring() != null) {
575                        newInvoke.setStateDuring(arraycopy.stateDuring());
576                    } else {
577                        assert arraycopy.stateAfter() != null;
578                        newInvoke.setStateAfter(arraycopy.stateAfter());
579                    }
580                    graph.replaceFixedWithFixed((InvokeNode) invoke.asNode(), newInvoke);
581                } else if (originalNode instanceof ArrayCopySlowPathNode) {
582                    ArrayCopySlowPathNode slowPath = (ArrayCopySlowPathNode) replacements.get(originalNode);
583                    assert arraycopy.stateAfter() != null;
584                    slowPath.setStateAfter(arraycopy.stateAfter());
585                    slowPath.setBci(arraycopy.getBci());
586                }
587            }
588        }
589    }
590}