1 /*
   2  * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 //JaCoCo Exclude
  24 package org.graalvm.compiler.hotspot.replacements.arraycopy;
  25 
  26 import static org.graalvm.compiler.nodeinfo.InputType.Memory;
  27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
  28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
  29 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayBaseOffset;
  30 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale;
  31 
  32 import org.graalvm.compiler.core.common.LocationIdentity;
  33 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  34 import org.graalvm.compiler.core.common.type.Stamp;
  35 import org.graalvm.compiler.core.common.type.StampFactory;
  36 import org.graalvm.compiler.graph.Node;
  37 import org.graalvm.compiler.graph.NodeClass;
  38 import org.graalvm.compiler.graph.spi.Canonicalizable;
  39 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  40 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
  41 import org.graalvm.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
  42 import org.graalvm.compiler.hotspot.nodes.GetObjectAddressNode;
  43 import org.graalvm.compiler.nodeinfo.InputType;
  44 import org.graalvm.compiler.nodeinfo.NodeInfo;
  45 import org.graalvm.compiler.nodes.ConstantNode;
  46 import org.graalvm.compiler.nodes.FixedWithNextNode;
  47 import org.graalvm.compiler.nodes.NamedLocationIdentity;
  48 import org.graalvm.compiler.nodes.StructuredGraph;
  49 import org.graalvm.compiler.nodes.ValueNode;
  50 import org.graalvm.compiler.nodes.calc.AddNode;
  51 import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
  52 import org.graalvm.compiler.nodes.calc.LeftShiftNode;
  53 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
  54 import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
  55 import org.graalvm.compiler.nodes.memory.MemoryAccess;
  56 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
  57 import org.graalvm.compiler.nodes.memory.MemoryNode;
  58 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
  59 import org.graalvm.compiler.nodes.spi.Lowerable;
  60 import org.graalvm.compiler.nodes.spi.LoweringTool;
  61 
  62 import jdk.vm.ci.code.CodeUtil;
  63 import jdk.vm.ci.meta.JavaConstant;
  64 import jdk.vm.ci.meta.JavaKind;
  65 import jdk.vm.ci.meta.PrimitiveConstant;
  66 
  67 @NodeInfo(allowedUsageTypes = {Memory}, cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN)
  68 public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, MemoryAccess, Canonicalizable {
  69 
  70     public static final NodeClass<ArrayCopyCallNode> TYPE = NodeClass.create(ArrayCopyCallNode.class);
  71     @Input protected ValueNode src;
  72     @Input protected ValueNode srcPos;
  73     @Input protected ValueNode dest;
  74     @Input protected ValueNode destPos;
  75     @Input protected ValueNode length;
  76 
  77     @OptionalInput(Memory) MemoryNode lastLocationAccess;
  78 
  79     protected final JavaKind elementKind;
  80     protected final LocationIdentity locationIdentity;
  81 
  82     /**
  83      * Aligned means that the offset of the copy is heap word aligned.
  84      */
  85     protected boolean aligned;
  86     protected boolean disjoint;
  87     protected boolean uninitialized;
  88 
  89     protected final HotSpotGraalRuntimeProvider runtime;
  90 
  91     public ArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, JavaKind elementKind,
  92                     boolean aligned, boolean disjoint, boolean uninitialized) {
  93         this(runtime, src, srcPos, dest, destPos, length, elementKind, null, aligned, disjoint, uninitialized);
  94     }
  95 
  96     public ArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, JavaKind elementKind,
  97                     boolean disjoint) {
  98         this(runtime, src, srcPos, dest, destPos, length, elementKind, null, false, disjoint, false);
  99     }
 100 
 101     protected ArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, JavaKind elementKind,
 102                     LocationIdentity locationIdentity, boolean aligned, boolean disjoint, boolean uninitialized) {
 103         super(TYPE, StampFactory.forVoid());
 104         assert elementKind != null;
 105         this.src = src;
 106         this.srcPos = srcPos;
 107         this.dest = dest;
 108         this.destPos = destPos;
 109         this.length = length;
 110         this.elementKind = elementKind;
 111         this.locationIdentity = (locationIdentity != null ? locationIdentity : NamedLocationIdentity.getArrayLocation(elementKind));
 112         this.aligned = aligned;
 113         this.disjoint = disjoint;
 114         this.uninitialized = uninitialized;
 115         this.runtime = runtime;
 116     }
 117 
 118     public ValueNode getSource() {
 119         return src;
 120     }
 121 
 122     public ValueNode getSourcePosition() {
 123         return srcPos;
 124     }
 125 
 126     public ValueNode getDestination() {
 127         return dest;
 128     }
 129 
 130     public ValueNode getDestinationPosition() {
 131         return destPos;
 132     }
 133 
 134     public ValueNode getLength() {
 135         return length;
 136     }
 137 
 138     public JavaKind getElementKind() {
 139         return elementKind;
 140     }
 141 
 142     private ValueNode computeBase(ValueNode base, ValueNode pos) {
 143         FixedWithNextNode basePtr = graph().add(new GetObjectAddressNode(base));
 144         graph().addBeforeFixed(this, basePtr);
 145         Stamp wordStamp = StampFactory.forKind(runtime.getTarget().wordJavaKind);
 146         ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph());
 147         int shift = CodeUtil.log2(getArrayIndexScale(elementKind));
 148         ValueNode scaledIndex = graph().unique(new LeftShiftNode(wordPos, ConstantNode.forInt(shift, graph())));
 149         ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerStamp(wordStamp, getArrayBaseOffset(elementKind), graph())));
 150         return graph().unique(new OffsetAddressNode(basePtr, offset));
 151     }
 152 
 153     @Override
 154     public void lower(LoweringTool tool) {
 155         if (graph().getGuardsStage().areFrameStatesAtDeopts()) {
 156             updateAlignedDisjoint();
 157             ForeignCallDescriptor desc = HotSpotHostForeignCallsProvider.lookupArraycopyDescriptor(elementKind, isAligned(), isDisjoint(), isUninitialized(),
 158                             locationIdentity.equals(LocationIdentity.any()));
 159             StructuredGraph graph = graph();
 160             ValueNode srcAddr = computeBase(getSource(), getSourcePosition());
 161             ValueNode destAddr = computeBase(getDestination(), getDestinationPosition());
 162             ValueNode len = getLength();
 163             if (len.stamp().getStackKind() != JavaKind.Long) {
 164                 len = IntegerConvertNode.convert(len, StampFactory.forKind(JavaKind.Long), graph());
 165             }
 166             ForeignCallNode call = graph.add(new ForeignCallNode(runtime.getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len));
 167             call.setStateAfter(stateAfter());
 168             graph.replaceFixedWithFixed(this, call);
 169         }
 170     }
 171 
 172     @Override
 173     public MemoryNode getLastLocationAccess() {
 174         return lastLocationAccess;
 175     }
 176 
 177     @Override
 178     public void setLastLocationAccess(MemoryNode lla) {
 179         updateUsagesInterface(lastLocationAccess, lla);
 180         lastLocationAccess = lla;
 181     }
 182 
 183     @Override
 184     public LocationIdentity getLocationIdentity() {
 185         return locationIdentity;
 186     }
 187 
 188     @NodeIntrinsic
 189     private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter boolean aligned,
 190                     @ConstantNodeParameter boolean disjoint, @ConstantNodeParameter boolean uninitialized);
 191 
 192     @NodeIntrinsic
 193     private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind,
 194                     @ConstantNodeParameter LocationIdentity locationIdentity, @ConstantNodeParameter boolean aligned, @ConstantNodeParameter boolean disjoint,
 195                     @ConstantNodeParameter boolean uninitialized);
 196 
 197     public static void arraycopyObjectKillsAny(Object src, int srcPos, Object dest, int destPos, int length) {
 198         arraycopy(src, srcPos, dest, destPos, length, JavaKind.Object, LocationIdentity.any(), false, false, false);
 199     }
 200 
 201     public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind) {
 202         arraycopy(src, srcPos, dest, destPos, length, elementKind, false, false, false);
 203     }
 204 
 205     public static void disjointArraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind) {
 206         arraycopy(src, srcPos, dest, destPos, length, elementKind, false, true, false);
 207     }
 208 
 209     public static void disjointUninitializedArraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind) {
 210         arraycopy(src, srcPos, dest, destPos, length, elementKind, false, true, true);
 211     }
 212 
 213     public boolean isAligned() {
 214         return aligned;
 215     }
 216 
 217     public boolean isDisjoint() {
 218         return disjoint;
 219     }
 220 
 221     public boolean isUninitialized() {
 222         return uninitialized;
 223     }
 224 
 225     boolean isHeapWordAligned(JavaConstant value, JavaKind kind) {
 226         return (getArrayBaseOffset(kind) + (long) value.asInt() * getArrayIndexScale(kind)) % runtime.getVMConfig().heapWordSize == 0;
 227     }
 228 
 229     public void updateAlignedDisjoint() {
 230         JavaKind componentKind = elementKind;
 231         if (srcPos == destPos) {
 232             // Can treat as disjoint
 233             disjoint = true;
 234         }
 235         PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp().asConstant();
 236         PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp().asConstant();
 237         if (constantSrc != null && constantDst != null) {
 238             if (!aligned) {
 239                 aligned = isHeapWordAligned(constantSrc, componentKind) && isHeapWordAligned(constantDst, componentKind);
 240             }
 241             if (constantSrc.asInt() >= constantDst.asInt()) {
 242                 // low to high copy so treat as disjoint
 243                 disjoint = true;
 244             }
 245         }
 246     }
 247 
 248     @Override
 249     public Node canonical(CanonicalizerTool tool) {
 250         if (getLength().isConstant() && getLength().asConstant().isDefaultForKind()) {
 251             if (lastLocationAccess != null) {
 252                 replaceAtUsages(InputType.Memory, lastLocationAccess.asNode());
 253             }
 254             return null;
 255         }
 256         return this;
 257     }
 258 }