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 */
023package com.oracle.graal.truffle.hotspot.nfi;
024
025import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
026
027import java.util.*;
028
029import jdk.internal.jvmci.common.*;
030import jdk.internal.jvmci.hotspot.*;
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.compiler.common.type.*;
034import com.oracle.graal.hotspot.meta.*;
035import com.oracle.graal.nodes.*;
036import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
037import com.oracle.graal.nodes.extended.*;
038import com.oracle.graal.nodes.java.*;
039import com.oracle.graal.nodes.memory.address.*;
040import com.oracle.graal.nodes.virtual.*;
041import com.oracle.graal.word.nodes.*;
042
043/**
044 * Utility creating a graph for a stub used to call a native function.
045 */
046public class NativeCallStubGraphBuilder {
047
048    /**
049     * Creates a graph for a stub used to call a native function.
050     *
051     * @param functionPointer a native function pointer
052     * @param returnType the type of the return value
053     * @param argumentTypes the types of the arguments
054     * @return the graph that represents the call stub
055     */
056    public static StructuredGraph getGraph(HotSpotProviders providers, RawNativeCallNodeFactory factory, long functionPointer, Class<?> returnType, Class<?>... argumentTypes) {
057        try {
058            ResolvedJavaMethod method = providers.getMetaAccess().lookupJavaMethod(NativeCallStubGraphBuilder.class.getMethod("libCall", Object.class, Object.class, Object.class));
059            StructuredGraph g = new StructuredGraph(method, AllowAssumptions.NO);
060            ParameterNode arg0 = g.unique(new ParameterNode(0, StampFactory.forKind(Kind.Object)));
061            ParameterNode arg1 = g.unique(new ParameterNode(1, StampFactory.forKind(Kind.Object)));
062            ParameterNode arg2 = g.unique(new ParameterNode(2, StampFactory.forKind(Kind.Object)));
063            FrameState frameState = g.add(new FrameState(null, method, 0, Arrays.asList(new ValueNode[]{arg0, arg1, arg2}), 3, 0, false, false, null, new ArrayList<EscapeObjectState>()));
064            g.start().setStateAfter(frameState);
065            List<ValueNode> parameters = new ArrayList<>();
066            FixedWithNextNode fixedWithNext = getParameters(g, arg0, argumentTypes.length, argumentTypes, parameters, providers);
067            JavaConstant functionPointerNode = JavaConstant.forLong(functionPointer);
068
069            ValueNode[] arguments = new ValueNode[parameters.size()];
070
071            for (int i = 0; i < arguments.length; i++) {
072                arguments[i] = parameters.get(i);
073            }
074
075            FixedWithNextNode callNode = g.add(factory.createRawCallNode(getKind(returnType), functionPointerNode, arguments));
076
077            if (fixedWithNext == null) {
078                g.start().setNext(callNode);
079            } else {
080                fixedWithNext.setNext(callNode);
081            }
082
083            // box result
084            BoxNode boxedResult;
085            if (callNode.getKind() != Kind.Void) {
086                if (callNode.getKind() == Kind.Object) {
087                    throw new IllegalArgumentException("Return type not supported: " + returnType.getName());
088                }
089                ResolvedJavaType type = providers.getMetaAccess().lookupJavaType(callNode.getKind().toBoxedJavaClass());
090                boxedResult = g.add(new BoxNode(callNode, type, callNode.getKind()));
091            } else {
092                boxedResult = g.add(new BoxNode(ConstantNode.forLong(0, g), providers.getMetaAccess().lookupJavaType(Long.class), Kind.Long));
093            }
094
095            callNode.setNext(boxedResult);
096            ReturnNode returnNode = g.add(new ReturnNode(boxedResult));
097            boxedResult.setNext(returnNode);
098            return g;
099        } catch (NoSuchMethodException e) {
100            throw JVMCIError.shouldNotReachHere("Call Stub method not found");
101        }
102    }
103
104    private static FixedWithNextNode getParameters(StructuredGraph g, ParameterNode argumentsArray, int numArgs, Class<?>[] argumentTypes, List<ValueNode> args, HotSpotProviders providers) {
105        assert numArgs == argumentTypes.length;
106        FixedWithNextNode last = null;
107        for (int i = 0; i < numArgs; i++) {
108            // load boxed array element:
109            LoadIndexedNode boxedElement = g.add(new LoadIndexedNode(argumentsArray, ConstantNode.forInt(i, g), Kind.Object));
110            if (i == 0) {
111                g.start().setNext(boxedElement);
112                last = boxedElement;
113            } else {
114                last.setNext(boxedElement);
115                last = boxedElement;
116            }
117            Class<?> type = argumentTypes[i];
118            Kind kind = getKind(type);
119            if (kind == Kind.Object) {
120                // array value
121                Kind arrayElementKind = getElementKind(type);
122                HotSpotJVMCIRuntimeProvider jvmciRuntime = runtime().getJVMCIRuntime();
123                int displacement = jvmciRuntime.getArrayBaseOffset(arrayElementKind);
124                AddressNode arrayAddress = g.unique(new OffsetAddressNode(boxedElement, ConstantNode.forLong(displacement, g)));
125                WordCastNode cast = g.add(WordCastNode.addressToWord(arrayAddress, providers.getWordTypes().getWordKind()));
126                last.setNext(cast);
127                last = cast;
128                args.add(cast);
129            } else {
130                // boxed primitive value
131                try {
132                    ResolvedJavaField field = providers.getMetaAccess().lookupJavaField(kind.toBoxedJavaClass().getDeclaredField("value"));
133                    LoadFieldNode loadFieldNode = g.add(new LoadFieldNode(boxedElement, field));
134                    last.setNext(loadFieldNode);
135                    last = loadFieldNode;
136                    args.add(loadFieldNode);
137                } catch (NoSuchFieldException e) {
138                    throw new JVMCIError(e);
139                }
140            }
141        }
142        return last;
143    }
144
145    public static Kind getElementKind(Class<?> clazz) {
146        Class<?> componentType = clazz.getComponentType();
147        if (componentType == null) {
148            throw new IllegalArgumentException("Parameter type not supported: " + clazz);
149        }
150        if (componentType.isPrimitive()) {
151            return Kind.fromJavaClass(componentType);
152        }
153        throw new IllegalArgumentException("Parameter type not supported: " + clazz);
154    }
155
156    private static Kind getKind(Class<?> clazz) {
157        if (clazz == int.class || clazz == Integer.class) {
158            return Kind.Int;
159        } else if (clazz == long.class || clazz == Long.class) {
160            return Kind.Long;
161        } else if (clazz == char.class || clazz == Character.class) {
162            return Kind.Char;
163        } else if (clazz == byte.class || clazz == Byte.class) {
164            return Kind.Byte;
165        } else if (clazz == float.class || clazz == Float.class) {
166            return Kind.Float;
167        } else if (clazz == double.class || clazz == Double.class) {
168            return Kind.Double;
169        } else if (clazz == int[].class || clazz == long[].class || clazz == char[].class || clazz == byte[].class || clazz == float[].class || clazz == double[].class) {
170            return Kind.Object;
171        } else if (clazz == void.class) {
172            return Kind.Void;
173        } else {
174            throw new IllegalArgumentException("Type not supported: " + clazz);
175        }
176    }
177
178    @SuppressWarnings("unused")
179    public static Object libCall(Object argLoc, Object unused1, Object unused2) {
180        throw JVMCIError.shouldNotReachHere("GNFI libCall method must not be called");
181    }
182}