1 /*
   2  * Copyright (c) 2014, 2018, 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 
  25 
  26 package org.graalvm.compiler.replacements.arraycopy;
  27 
  28 import static org.graalvm.compiler.nodeinfo.InputType.Memory;
  29 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
  30 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
  31 
  32 import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
  33 import org.graalvm.compiler.core.common.type.Stamp;
  34 import org.graalvm.compiler.core.common.type.StampFactory;
  35 import org.graalvm.compiler.graph.Node;
  36 import org.graalvm.compiler.graph.NodeClass;
  37 import org.graalvm.compiler.graph.spi.Canonicalizable;
  38 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  39 import org.graalvm.compiler.nodeinfo.InputType;
  40 import org.graalvm.compiler.nodeinfo.NodeInfo;
  41 import org.graalvm.compiler.nodes.ConstantNode;
  42 import org.graalvm.compiler.nodes.FixedWithNextNode;
  43 import org.graalvm.compiler.nodes.GetObjectAddressNode;
  44 import org.graalvm.compiler.nodes.NamedLocationIdentity;
  45 import org.graalvm.compiler.nodes.NodeView;
  46 import org.graalvm.compiler.nodes.StructuredGraph;
  47 import org.graalvm.compiler.nodes.ValueNode;
  48 import org.graalvm.compiler.nodes.calc.AddNode;
  49 import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
  50 import org.graalvm.compiler.nodes.calc.LeftShiftNode;
  51 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
  52 import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
  53 import org.graalvm.compiler.nodes.memory.MemoryAccess;
  54 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
  55 import org.graalvm.compiler.nodes.memory.MemoryNode;
  56 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
  57 import org.graalvm.compiler.nodes.spi.Lowerable;
  58 import org.graalvm.compiler.nodes.spi.LoweringTool;
  59 import org.graalvm.compiler.word.WordTypes;
  60 import jdk.internal.vm.compiler.word.LocationIdentity;
  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.MetaAccessProvider;
  66 import jdk.vm.ci.meta.PrimitiveConstant;
  67 
  68 @NodeInfo(allowedUsageTypes = {Memory}, cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN)
  69 public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, MemoryAccess, Canonicalizable {
  70 
  71     public static final NodeClass<ArrayCopyCallNode> TYPE = NodeClass.create(ArrayCopyCallNode.class);
  72     @Input protected ValueNode src;
  73     @Input protected ValueNode srcPos;
  74     @Input protected ValueNode dest;
  75     @Input protected ValueNode destPos;
  76     @Input protected ValueNode length;
  77 
  78     @OptionalInput(Memory) MemoryNode lastLocationAccess;
  79 
  80     private final JavaKind elementKind;
  81     private final LocationIdentity locationIdentity;
  82     private final ArrayCopyForeignCalls foreignCalls;
  83     private final JavaKind wordJavaKind;
  84     private final int heapWordSize;
  85 
  86     /**
  87      * Aligned means that the offset of the copy is heap word aligned.
  88      */
  89     private boolean aligned;
  90     private boolean disjoint;
  91     private boolean uninitialized;
  92 
  93     public ArrayCopyCallNode(@InjectedNodeParameter ArrayCopyForeignCalls foreignCalls, @InjectedNodeParameter WordTypes wordTypes,
  94                     ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos,
  95                     ValueNode length, JavaKind elementKind, boolean aligned, boolean disjoint, boolean uninitialized, int heapWordSize) {
  96         this(foreignCalls, wordTypes, src, srcPos, dest, destPos, length, elementKind, null, aligned, disjoint, uninitialized, heapWordSize);
  97     }
  98 
  99     protected ArrayCopyCallNode(@InjectedNodeParameter ArrayCopyForeignCalls foreignCalls, @InjectedNodeParameter WordTypes wordTypes,
 100                     ValueNode src, ValueNode srcPos, ValueNode dest,
 101                     ValueNode destPos, ValueNode length, JavaKind elementKind,
 102                     LocationIdentity locationIdentity, boolean aligned, boolean disjoint, boolean uninitialized, int heapWordSize) {
 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.foreignCalls = foreignCalls;
 116         this.wordJavaKind = wordTypes.getWordKind();
 117         this.heapWordSize = heapWordSize;
 118 
 119     }
 120 
 121     public ValueNode getSource() {
 122         return src;
 123     }
 124 
 125     public ValueNode getSourcePosition() {
 126         return srcPos;
 127     }
 128 
 129     public ValueNode getDestination() {
 130         return dest;
 131     }
 132 
 133     public ValueNode getDestinationPosition() {
 134         return destPos;
 135     }
 136 
 137     public ValueNode getLength() {
 138         return length;
 139     }
 140 
 141     public JavaKind getElementKind() {
 142         return elementKind;
 143     }
 144 
 145     private ValueNode computeBase(LoweringTool tool, ValueNode base, ValueNode pos) {
 146         FixedWithNextNode basePtr = graph().add(new GetObjectAddressNode(base));
 147         graph().addBeforeFixed(this, basePtr);
 148         Stamp wordStamp = StampFactory.forKind(wordJavaKind);
 149         ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph(), NodeView.DEFAULT);
 150         int shift = CodeUtil.log2(tool.getMetaAccess().getArrayIndexScale(elementKind));
 151         ValueNode scaledIndex = graph().unique(new LeftShiftNode(wordPos, ConstantNode.forInt(shift, graph())));
 152         ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerStamp(wordStamp, tool.getMetaAccess().getArrayBaseOffset(elementKind), graph())));
 153         return graph().unique(new OffsetAddressNode(basePtr, offset));
 154     }
 155 
 156     @Override
 157     public void lower(LoweringTool tool) {
 158         if (graph().getGuardsStage().areFrameStatesAtDeopts()) {
 159             updateAlignedDisjoint(tool.getMetaAccess());
 160             ForeignCallDescriptor desc = foreignCalls.lookupArraycopyDescriptor(elementKind, isAligned(), isDisjoint(), isUninitialized(),
 161                             locationIdentity.equals(LocationIdentity.any()));
 162             StructuredGraph graph = graph();
 163             ValueNode srcAddr = computeBase(tool, getSource(), getSourcePosition());
 164             ValueNode destAddr = computeBase(tool, getDestination(), getDestinationPosition());
 165             ValueNode len = getLength();
 166             if (len.stamp(NodeView.DEFAULT).getStackKind() != JavaKind.Long) {
 167                 len = IntegerConvertNode.convert(len, StampFactory.forKind(JavaKind.Long), graph(), NodeView.DEFAULT);
 168             }
 169             ForeignCallNode call = graph.add(new ForeignCallNode(foreignCalls, desc, srcAddr, destAddr, len));
 170             call.setStateAfter(stateAfter());
 171             graph.replaceFixedWithFixed(this, call);
 172         }
 173     }
 174 
 175     @Override
 176     public MemoryNode getLastLocationAccess() {
 177         return lastLocationAccess;
 178     }
 179 
 180     @Override
 181     public void setLastLocationAccess(MemoryNode lla) {
 182         updateUsagesInterface(lastLocationAccess, lla);
 183         lastLocationAccess = lla;
 184     }
 185 
 186     @Override
 187     public LocationIdentity getLocationIdentity() {
 188         return locationIdentity;
 189     }
 190 
 191     @NodeIntrinsic
 192     private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter boolean aligned,
 193                     @ConstantNodeParameter boolean disjoint, @ConstantNodeParameter boolean uninitialized, @ConstantNodeParameter int heapWordSize);
 194 
 195     @NodeIntrinsic
 196     private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind,
 197                     @ConstantNodeParameter LocationIdentity locationIdentity, @ConstantNodeParameter boolean aligned, @ConstantNodeParameter boolean disjoint,
 198                     @ConstantNodeParameter boolean uninitialized, @ConstantNodeParameter int heapWordSize);
 199 
 200     public static void arraycopyObjectKillsAny(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter int heapWordSize) {
 201         arraycopy(src, srcPos, dest, destPos, length, JavaKind.Object, LocationIdentity.any(), false, false, false, heapWordSize);
 202     }
 203 
 204     public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter LocationIdentity locationIdentity,
 205                     @ConstantNodeParameter int heapWordSize) {
 206         arraycopy(src, srcPos, dest, destPos, length, elementKind, locationIdentity, false, false, false, heapWordSize);
 207     }
 208 
 209     public static void disjointArraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter int heapWordSize) {
 210         arraycopy(src, srcPos, dest, destPos, length, elementKind, false, true, false, heapWordSize);
 211     }
 212 
 213     public static void disjointArraycopyKillsAny(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter int heapWordSize) {
 214         arraycopy(src, srcPos, dest, destPos, length, elementKind, LocationIdentity.any(), false, true, false, heapWordSize);
 215     }
 216 
 217     public static void disjointUninitializedArraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind,
 218                     @ConstantNodeParameter int heapWordSize) {
 219         arraycopy(src, srcPos, dest, destPos, length, elementKind, false, true, true, heapWordSize);
 220     }
 221 
 222     public boolean isAligned() {
 223         return aligned;
 224     }
 225 
 226     public boolean isDisjoint() {
 227         return disjoint;
 228     }
 229 
 230     public boolean isUninitialized() {
 231         return uninitialized;
 232     }
 233 
 234     boolean isHeapWordAligned(MetaAccessProvider metaAccess, JavaConstant value, JavaKind kind) {
 235         return (metaAccess.getArrayBaseOffset(kind) + (long) value.asInt() * metaAccess.getArrayIndexScale(kind)) % heapWordSize == 0;
 236     }
 237 
 238     public void updateAlignedDisjoint(MetaAccessProvider metaAccess) {
 239         JavaKind componentKind = elementKind;
 240         if (srcPos == destPos) {
 241             // Can treat as disjoint
 242             disjoint = true;
 243         }
 244         PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp(NodeView.DEFAULT).asConstant();
 245         PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp(NodeView.DEFAULT).asConstant();
 246         if (constantSrc != null && constantDst != null) {
 247             if (!aligned) {
 248                 aligned = isHeapWordAligned(metaAccess, constantSrc, componentKind) && isHeapWordAligned(metaAccess, constantDst, componentKind);
 249             }
 250             if (constantSrc.asInt() >= constantDst.asInt()) {
 251                 // low to high copy so treat as disjoint
 252                 disjoint = true;
 253             }
 254         }
 255     }
 256 
 257     @Override
 258     public Node canonical(CanonicalizerTool tool) {
 259         if (getLength().isConstant() && getLength().asConstant().isDefaultForKind()) {
 260             if (lastLocationAccess != null) {
 261                 replaceAtUsages(InputType.Memory, lastLocationAccess.asNode());
 262             }
 263             return null;
 264         }
 265         return this;
 266     }
 267 }