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
24package org.graalvm.compiler.hotspot.replacements.arraycopy;
25
26import static org.graalvm.compiler.nodeinfo.InputType.Memory;
27import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
28import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
29import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayBaseOffset;
30import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayIndexScale;
31
32import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
33import org.graalvm.compiler.core.common.type.Stamp;
34import org.graalvm.compiler.core.common.type.StampFactory;
35import org.graalvm.compiler.graph.Node;
36import org.graalvm.compiler.graph.NodeClass;
37import org.graalvm.compiler.graph.spi.Canonicalizable;
38import org.graalvm.compiler.graph.spi.CanonicalizerTool;
39import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
40import org.graalvm.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
41import org.graalvm.compiler.hotspot.nodes.GetObjectAddressNode;
42import org.graalvm.compiler.nodeinfo.InputType;
43import org.graalvm.compiler.nodeinfo.NodeInfo;
44import org.graalvm.compiler.nodes.ConstantNode;
45import org.graalvm.compiler.nodes.FixedWithNextNode;
46import org.graalvm.compiler.nodes.NamedLocationIdentity;
47import org.graalvm.compiler.nodes.StructuredGraph;
48import org.graalvm.compiler.nodes.ValueNode;
49import org.graalvm.compiler.nodes.calc.AddNode;
50import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
51import org.graalvm.compiler.nodes.calc.LeftShiftNode;
52import org.graalvm.compiler.nodes.extended.ForeignCallNode;
53import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
54import org.graalvm.compiler.nodes.memory.MemoryAccess;
55import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
56import org.graalvm.compiler.nodes.memory.MemoryNode;
57import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
58import org.graalvm.compiler.nodes.spi.Lowerable;
59import org.graalvm.compiler.nodes.spi.LoweringTool;
60import org.graalvm.word.LocationIdentity;
61
62import jdk.vm.ci.code.CodeUtil;
63import jdk.vm.ci.meta.JavaConstant;
64import jdk.vm.ci.meta.JavaKind;
65import jdk.vm.ci.meta.PrimitiveConstant;
66
67@NodeInfo(allowedUsageTypes = {Memory}, cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN)
68public 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}
259