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(hasSideEffect = true) 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(hasSideEffect = true) 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 }