001/*
002 * Copyright (c) 2012, 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 */
023package com.oracle.graal.hotspot;
024
025import static com.oracle.graal.hotspot.HotSpotForeignCallLinkage.RegisterEffect.*;
026import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
027
028import java.util.*;
029
030import jdk.internal.jvmci.code.*;
031import jdk.internal.jvmci.code.CallingConvention.*;
032import jdk.internal.jvmci.hotspot.*;
033import jdk.internal.jvmci.meta.*;
034
035import com.oracle.graal.compiler.common.spi.*;
036import com.oracle.graal.compiler.target.*;
037import com.oracle.graal.hotspot.meta.*;
038import com.oracle.graal.hotspot.stubs.*;
039import com.oracle.graal.word.*;
040
041/**
042 * The details required to link a HotSpot runtime or stub call.
043 */
044public class HotSpotForeignCallLinkageImpl extends HotSpotForeignCallTarget implements HotSpotForeignCallLinkage, HotSpotProxified {
045
046    /**
047     * The descriptor of the call.
048     */
049    protected final ForeignCallDescriptor descriptor;
050
051    /**
052     * Non-null (eventually) iff this is a call to a compiled {@linkplain Stub stub}.
053     */
054    private Stub stub;
055
056    /**
057     * The calling convention for this call.
058     */
059    private final CallingConvention outgoingCallingConvention;
060
061    /**
062     * The calling convention for incoming arguments to the stub, iff this call uses a compiled
063     * {@linkplain Stub stub}.
064     */
065    private final CallingConvention incomingCallingConvention;
066
067    private final RegisterEffect effect;
068
069    private final Transition transition;
070
071    /**
072     * The registers and stack slots defined/killed by the call.
073     */
074    private Value[] temporaries = AllocatableValue.NONE;
075
076    /**
077     * The memory locations killed by the call.
078     */
079    private final LocationIdentity[] killedLocations;
080
081    private final boolean reexecutable;
082
083    /**
084     * Creates a {@link HotSpotForeignCallLinkage}.
085     *
086     * @param descriptor the descriptor of the call
087     * @param address the address of the code to call
088     * @param effect specifies if the call destroys or preserves all registers (apart from
089     *            temporaries which are always destroyed)
090     * @param outgoingCcType outgoing (caller) calling convention type
091     * @param incomingCcType incoming (callee) calling convention type (can be null)
092     * @param transition specifies if this is a {@linkplain #needsDebugInfo() leaf} call
093     * @param reexecutable specifies if the call can be re-executed without (meaningful) side
094     *            effects. Deoptimization will not return to a point before a call that cannot be
095     *            re-executed.
096     * @param killedLocations the memory locations killed by the call
097     */
098    public static HotSpotForeignCallLinkage create(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, HotSpotForeignCallsProvider foreignCalls, ForeignCallDescriptor descriptor,
099                    long address, RegisterEffect effect, Type outgoingCcType, Type incomingCcType, Transition transition, boolean reexecutable, LocationIdentity... killedLocations) {
100        CallingConvention outgoingCc = createCallingConvention(metaAccess, codeCache, descriptor, outgoingCcType);
101        CallingConvention incomingCc = incomingCcType == null ? null : createCallingConvention(metaAccess, codeCache, descriptor, incomingCcType);
102        HotSpotForeignCallLinkageImpl linkage = new HotSpotForeignCallLinkageImpl(descriptor, address, effect, transition, outgoingCc, incomingCc, reexecutable, killedLocations);
103        if (outgoingCcType == Type.NativeCall) {
104            linkage.temporaries = foreignCalls.getNativeABICallerSaveRegisters();
105        }
106        return linkage;
107    }
108
109    /**
110     * Gets a calling convention for a given descriptor and call type.
111     */
112    public static CallingConvention createCallingConvention(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, ForeignCallDescriptor descriptor, Type ccType) {
113        assert ccType != null;
114        Class<?>[] argumentTypes = descriptor.getArgumentTypes();
115        JavaType[] parameterTypes = new JavaType[argumentTypes.length];
116        for (int i = 0; i < parameterTypes.length; ++i) {
117            parameterTypes[i] = asJavaType(argumentTypes[i], metaAccess, codeCache);
118        }
119        TargetDescription target = codeCache.getTarget();
120        JavaType returnType = asJavaType(descriptor.getResultType(), metaAccess, codeCache);
121        RegisterConfig regConfig = codeCache.getRegisterConfig();
122        return regConfig.getCallingConvention(ccType, returnType, parameterTypes, target, false);
123    }
124
125    private static JavaType asJavaType(Class<?> type, MetaAccessProvider metaAccess, CodeCacheProvider codeCache) {
126        if (WordBase.class.isAssignableFrom(type)) {
127            return metaAccess.lookupJavaType(codeCache.getTarget().wordKind.toJavaClass());
128        } else {
129            return metaAccess.lookupJavaType(type);
130        }
131    }
132
133    public HotSpotForeignCallLinkageImpl(ForeignCallDescriptor descriptor, long address, RegisterEffect effect, Transition transition, CallingConvention outgoingCallingConvention,
134                    CallingConvention incomingCallingConvention, boolean reexecutable, LocationIdentity... killedLocations) {
135        super(address);
136        this.descriptor = descriptor;
137        this.address = address;
138        this.effect = effect;
139        this.transition = transition;
140        this.outgoingCallingConvention = outgoingCallingConvention;
141        this.incomingCallingConvention = incomingCallingConvention;
142        this.reexecutable = reexecutable;
143        this.killedLocations = killedLocations;
144    }
145
146    @Override
147    public String toString() {
148        StringBuilder sb = new StringBuilder(stub == null ? descriptor.toString() : stub.toString());
149        sb.append("@0x").append(Long.toHexString(address)).append(':').append(outgoingCallingConvention).append(":").append(incomingCallingConvention);
150        if (temporaries != null && temporaries.length != 0) {
151            sb.append("; temps=");
152            String sep = "";
153            for (Value op : temporaries) {
154                sb.append(sep).append(op);
155                sep = ",";
156            }
157        }
158        return sb.toString();
159    }
160
161    public boolean isReexecutable() {
162        return reexecutable;
163    }
164
165    public LocationIdentity[] getKilledLocations() {
166        return killedLocations;
167    }
168
169    public CallingConvention getOutgoingCallingConvention() {
170        return outgoingCallingConvention;
171    }
172
173    public CallingConvention getIncomingCallingConvention() {
174        return incomingCallingConvention;
175    }
176
177    public Value[] getTemporaries() {
178        if (temporaries.length == 0) {
179            return temporaries;
180        }
181        return temporaries.clone();
182    }
183
184    public long getMaxCallTargetOffset() {
185        return runtime().getCompilerToVM().getMaxCallTargetOffset(address);
186    }
187
188    public ForeignCallDescriptor getDescriptor() {
189        return descriptor;
190    }
191
192    public void setCompiledStub(Stub stub) {
193        assert address == 0L : "cannot set stub for linkage that already has an address: " + this;
194        this.stub = stub;
195    }
196
197    /**
198     * Determines if this is a call to a compiled {@linkplain Stub stub}.
199     */
200    public boolean isCompiledStub() {
201        return address == 0L || stub != null;
202    }
203
204    public void finalizeAddress(Backend backend) {
205        if (address == 0) {
206            assert stub != null : "linkage without an address must be a stub - forgot to register a Stub associated with " + descriptor + "?";
207            InstalledCode code = stub.getCode(backend);
208
209            Set<Register> destroyedRegisters = stub.getDestroyedRegisters();
210            if (!destroyedRegisters.isEmpty()) {
211                AllocatableValue[] temporaryLocations = new AllocatableValue[destroyedRegisters.size()];
212                int i = 0;
213                for (Register reg : destroyedRegisters) {
214                    temporaryLocations[i++] = reg.asValue();
215                }
216                temporaries = temporaryLocations;
217            }
218            address = code.getStart();
219        }
220    }
221
222    public long getAddress() {
223        assert address != 0L : "address not yet finalized: " + this;
224        return address;
225    }
226
227    @Override
228    public boolean destroysRegisters() {
229        return effect == DESTROYS_REGISTERS;
230    }
231
232    @Override
233    public boolean needsDebugInfo() {
234        return transition == Transition.NOT_LEAF;
235    }
236
237    public boolean mayContainFP() {
238        return transition != Transition.LEAF_NOFP;
239    }
240
241    public boolean needsJavaFrameAnchor() {
242        if (transition == Transition.NOT_LEAF || transition == Transition.STACK_INSPECTABLE_LEAF) {
243            if (stub != null) {
244                // The stub will do the JavaFrameAnchor management
245                // around the runtime call(s) it makes
246                return false;
247            } else {
248                return true;
249            }
250        }
251        return false;
252    }
253
254    public String getSymbol() {
255        return stub == null ? null : stub.toString();
256    }
257}