001/*
002 * Copyright (c) 2014, 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 */
023//JaCoCo Exclude
024package com.oracle.graal.hotspot.replacements.arraycopy;
025
026import jdk.internal.jvmci.code.*;
027import jdk.internal.jvmci.hotspot.*;
028import jdk.internal.jvmci.meta.*;
029
030import com.oracle.graal.api.runtime.*;
031import com.oracle.graal.compiler.common.spi.*;
032import com.oracle.graal.compiler.common.type.*;
033import com.oracle.graal.graph.*;
034import com.oracle.graal.graph.spi.*;
035import com.oracle.graal.hotspot.*;
036import com.oracle.graal.hotspot.meta.*;
037import com.oracle.graal.hotspot.nodes.*;
038import com.oracle.graal.nodeinfo.*;
039import com.oracle.graal.nodes.*;
040import com.oracle.graal.nodes.calc.*;
041import com.oracle.graal.nodes.extended.*;
042import com.oracle.graal.nodes.memory.*;
043import com.oracle.graal.nodes.memory.address.*;
044import com.oracle.graal.nodes.spi.*;
045import com.oracle.graal.runtime.*;
046
047@NodeInfo(allowedUsageTypes = {InputType.Memory})
048public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, MemoryAccess, Canonicalizable {
049
050    public static final NodeClass<ArrayCopyCallNode> TYPE = NodeClass.create(ArrayCopyCallNode.class);
051    @Input protected ValueNode src;
052    @Input protected ValueNode srcPos;
053    @Input protected ValueNode dest;
054    @Input protected ValueNode destPos;
055    @Input protected ValueNode length;
056
057    @OptionalInput(InputType.Memory) MemoryNode lastLocationAccess;
058
059    protected final Kind elementKind;
060    protected final LocationIdentity locationIdentity;
061
062    /**
063     * Aligned means that the offset of the copy is heap word aligned.
064     */
065    protected boolean aligned;
066    protected boolean disjoint;
067    protected boolean uninitialized;
068
069    protected final HotSpotGraalRuntimeProvider runtime;
070
071    public ArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, Kind elementKind,
072                    boolean aligned, boolean disjoint, boolean uninitialized) {
073        this(runtime, src, srcPos, dest, destPos, length, elementKind, null, aligned, disjoint, uninitialized);
074    }
075
076    public ArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, Kind elementKind,
077                    boolean disjoint) {
078        this(runtime, src, srcPos, dest, destPos, length, elementKind, null, false, disjoint, false);
079    }
080
081    protected ArrayCopyCallNode(@InjectedNodeParameter HotSpotGraalRuntimeProvider runtime, ValueNode src, ValueNode srcPos, ValueNode dest, ValueNode destPos, ValueNode length, Kind elementKind,
082                    LocationIdentity locationIdentity, boolean aligned, boolean disjoint, boolean uninitialized) {
083        super(TYPE, StampFactory.forVoid());
084        assert elementKind != null;
085        this.src = src;
086        this.srcPos = srcPos;
087        this.dest = dest;
088        this.destPos = destPos;
089        this.length = length;
090        this.elementKind = elementKind;
091        this.locationIdentity = (locationIdentity != null ? locationIdentity : NamedLocationIdentity.getArrayLocation(elementKind));
092        this.aligned = aligned;
093        this.disjoint = disjoint;
094        this.uninitialized = uninitialized;
095        this.runtime = runtime;
096    }
097
098    public ValueNode getSource() {
099        return src;
100    }
101
102    public ValueNode getSourcePosition() {
103        return srcPos;
104    }
105
106    public ValueNode getDestination() {
107        return dest;
108    }
109
110    public ValueNode getDestinationPosition() {
111        return destPos;
112    }
113
114    public ValueNode getLength() {
115        return length;
116    }
117
118    public Kind getElementKind() {
119        return elementKind;
120    }
121
122    private ValueNode computeBase(ValueNode base, ValueNode pos) {
123        FixedWithNextNode basePtr = graph().add(new GetObjectAddressNode(base));
124        graph().addBeforeFixed(this, basePtr);
125        HotSpotJVMCIRuntimeProvider jvmciRuntime = runtime.getJVMCIRuntime();
126        Stamp wordStamp = StampFactory.forKind(runtime.getTarget().wordKind);
127        ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph());
128        int shift = CodeUtil.log2(jvmciRuntime.getArrayIndexScale(elementKind));
129        ValueNode scaledIndex = graph().unique(new LeftShiftNode(wordPos, ConstantNode.forInt(shift, graph())));
130        ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerStamp(wordStamp, jvmciRuntime.getArrayBaseOffset(elementKind), graph())));
131        return graph().unique(new OffsetAddressNode(basePtr, offset));
132    }
133
134    @Override
135    public void lower(LoweringTool tool) {
136        if (graph().getGuardsStage().areFrameStatesAtDeopts()) {
137            updateAlignedDisjoint();
138            ForeignCallDescriptor desc = HotSpotHostForeignCallsProvider.lookupArraycopyDescriptor(elementKind, isAligned(), isDisjoint(), isUninitialized(),
139                            locationIdentity.equals(LocationIdentity.any()));
140            StructuredGraph graph = graph();
141            ValueNode srcAddr = computeBase(getSource(), getSourcePosition());
142            ValueNode destAddr = computeBase(getDestination(), getDestinationPosition());
143            ValueNode len = getLength();
144            if (len.stamp().getStackKind() != Kind.Long) {
145                len = IntegerConvertNode.convert(len, StampFactory.forKind(Kind.Long), graph());
146            }
147            ForeignCallNode call = graph.add(new ForeignCallNode(Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len));
148            call.setStateAfter(stateAfter());
149            graph.replaceFixedWithFixed(this, call);
150        }
151    }
152
153    public MemoryNode getLastLocationAccess() {
154        return lastLocationAccess;
155    }
156
157    public void setLastLocationAccess(MemoryNode lla) {
158        updateUsagesInterface(lastLocationAccess, lla);
159        lastLocationAccess = lla;
160    }
161
162    @Override
163    public LocationIdentity getLocationIdentity() {
164        return locationIdentity;
165    }
166
167    @NodeIntrinsic
168    private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter Kind elementKind, @ConstantNodeParameter boolean aligned,
169                    @ConstantNodeParameter boolean disjoint, @ConstantNodeParameter boolean uninitialized);
170
171    @NodeIntrinsic
172    private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter Kind elementKind,
173                    @ConstantNodeParameter LocationIdentity locationIdentity, @ConstantNodeParameter boolean aligned, @ConstantNodeParameter boolean disjoint,
174                    @ConstantNodeParameter boolean uninitialized);
175
176    public static void arraycopyObjectKillsAny(Object src, int srcPos, Object dest, int destPos, int length) {
177        arraycopy(src, srcPos, dest, destPos, length, Kind.Object, LocationIdentity.any(), false, false, false);
178    }
179
180    public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter Kind elementKind) {
181        arraycopy(src, srcPos, dest, destPos, length, elementKind, false, false, false);
182    }
183
184    public static void disjointArraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter Kind elementKind) {
185        arraycopy(src, srcPos, dest, destPos, length, elementKind, false, true, false);
186    }
187
188    public static void disjointUninitializedArraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter Kind elementKind) {
189        arraycopy(src, srcPos, dest, destPos, length, elementKind, false, true, true);
190    }
191
192    public boolean isAligned() {
193        return aligned;
194    }
195
196    public boolean isDisjoint() {
197        return disjoint;
198    }
199
200    public boolean isUninitialized() {
201        return uninitialized;
202    }
203
204    boolean isHeapWordAligned(JavaConstant value, Kind kind) {
205        HotSpotJVMCIRuntimeProvider jvmciRuntime = runtime.getJVMCIRuntime();
206        return (jvmciRuntime.getArrayBaseOffset(kind) + (long) value.asInt() * jvmciRuntime.getArrayIndexScale(kind)) % runtime.getConfig().heapWordSize == 0;
207    }
208
209    public void updateAlignedDisjoint() {
210        Kind componentKind = elementKind;
211        if (srcPos == destPos) {
212            // Can treat as disjoint
213            disjoint = true;
214        }
215        PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp().asConstant();
216        PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp().asConstant();
217        if (constantSrc != null && constantDst != null) {
218            if (!aligned) {
219                aligned = isHeapWordAligned(constantSrc, componentKind) && isHeapWordAligned(constantDst, componentKind);
220            }
221            if (constantSrc.asInt() >= constantDst.asInt()) {
222                // low to high copy so treat as disjoint
223                disjoint = true;
224            }
225        }
226    }
227
228    @Override
229    public Node canonical(CanonicalizerTool tool) {
230        if (getLength().isConstant() && getLength().asConstant().isDefaultForKind()) {
231            if (lastLocationAccess != null) {
232                replaceAtUsages(InputType.Memory, lastLocationAccess.asNode());
233            }
234            return null;
235        }
236        return this;
237    }
238}