view graal/com.oracle.graal.nfi.hotspot.amd64/src/com/oracle/graal/nfi/hotspot/amd64/util/NativeCallStubGraphBuilder.java @ 13872:43678ad7ae92

GNFI: rename project from .ffi.amd64 to .nfi.hotspot.amd64
author Matthias Grimmer <grimmer@ssw.jku.at>
date Wed, 05 Feb 2014 10:38:12 +0100
parents graal/com.oracle.graal.ffi.amd64/src/com/oracle/graal/ffi/amd64/util/NativeCallStubGraphBuilder.java@ed3a1471e133
children
line wrap: on
line source

/*
 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.graal.nfi.hotspot.amd64.util;

import java.util.*;

import sun.misc.*;

import com.oracle.graal.api.meta.*;
import com.oracle.graal.graph.*;
import com.oracle.graal.hotspot.*;
import com.oracle.graal.hotspot.meta.*;
import com.oracle.graal.nfi.hotspot.amd64.*;
import com.oracle.graal.nfi.hotspot.amd64.node.*;
import com.oracle.graal.nodes.*;
import com.oracle.graal.nodes.extended.*;
import com.oracle.graal.nodes.java.*;
import com.oracle.graal.nodes.type.*;
import com.oracle.graal.nodes.virtual.*;
import com.oracle.graal.word.phases.*;

/**
 * Utility that creates a Graal graph. The graph represents the native call stub for a foreign
 * target function.
 * 
 */
public class NativeCallStubGraphBuilder {

    private static final ResolvedJavaType integerJavaType = HotSpotResolvedObjectType.fromClass(Integer.class);
    private static final ResolvedJavaField[] integerInstanceFields = integerJavaType.getInstanceFields(false);
    private static final ResolvedJavaField integerValueField = getValueField(integerInstanceFields);

    private static final ResolvedJavaType longJavaType = HotSpotResolvedObjectType.fromClass(Long.class);
    private static final ResolvedJavaField[] longInstanceFields = longJavaType.getInstanceFields(false);
    private static final ResolvedJavaField longValueField = getValueField(longInstanceFields);

    private static final ResolvedJavaType charJavaType = HotSpotResolvedObjectType.fromClass(Character.class);
    private static final ResolvedJavaField[] charInstanceFields = charJavaType.getInstanceFields(false);
    private static final ResolvedJavaField charValueField = getValueField(charInstanceFields);

    private static final ResolvedJavaType byteJavaType = HotSpotResolvedObjectType.fromClass(Byte.class);
    private static final ResolvedJavaField[] byteInstanceFields = byteJavaType.getInstanceFields(false);
    private static final ResolvedJavaField byteValueField = getValueField(byteInstanceFields);

    private static final ResolvedJavaType floatJavaType = HotSpotResolvedObjectType.fromClass(Float.class);
    private static final ResolvedJavaField[] floatInstanceFields = floatJavaType.getInstanceFields(false);
    private static final ResolvedJavaField floatValueField = getValueField(floatInstanceFields);

    private static final ResolvedJavaType doubleJavaType = HotSpotResolvedObjectType.fromClass(Double.class);
    private static final ResolvedJavaField[] doubleInstanceFields = doubleJavaType.getInstanceFields(false);
    private static final ResolvedJavaField doubleValueField = getValueField(doubleInstanceFields);

    private static ResolvedJavaField getValueField(ResolvedJavaField[] fields) {
        for (ResolvedJavaField field : fields) {
            if (field.getName().equals("value")) {
                return field;
            }
        }
        throw new AssertionError("value field not found!");
    }

    /**
     * Creates a Graal graph that represents the call stub for a foreign target function.
     * 
     * @param providers the HotSpot providers
     * @param functionPointer a function pointer that points to the foreign target function
     * @param returnType the type of the return value
     * @param argumentTypes the types of the arguments
     * @return the graph that represents the call stub
     */
    public static StructuredGraph getGraph(HotSpotProviders providers, AMD64HotSpotNativeFunctionPointer functionPointer, Class returnType, Class[] argumentTypes) {
        ResolvedJavaMethod method;
        try {
            method = providers.getMetaAccess().lookupJavaMethod(NativeCallStubGraphBuilder.class.getMethod("libCall", Object.class, Object.class, Object.class));
            StructuredGraph g = new StructuredGraph(method);
            ParameterNode arg0 = g.unique(new ParameterNode(0, StampFactory.forKind(Kind.Object)));
            ParameterNode arg1 = g.unique(new ParameterNode(1, StampFactory.forKind(Kind.Object)));
            ParameterNode arg2 = g.unique(new ParameterNode(2, StampFactory.forKind(Kind.Object)));
            FrameState frameState = g.add(new FrameState(method, 0, Arrays.asList(new ValueNode[]{arg0, arg1, arg2}), 3, 0, false, false, new ArrayList<MonitorIdNode>(),
                            new ArrayList<EscapeObjectState>()));
            g.start().setStateAfter(frameState);
            List<ValueNode> parameters = new ArrayList<>();
            FixedWithNextNode fixedWithNext = getParameters(g, arg0, argumentTypes.length, argumentTypes, parameters, providers);
            Constant functionPointerNode = Constant.forLong(functionPointer.asRawValue());

            ValueNode[] arguments = new ValueNode[parameters.size()];

            for (int i = 0; i < arguments.length; i++) {
                arguments[i] = parameters.get(i);
            }

            AMD64RawNativeCallNode callNode = g.add(new AMD64RawNativeCallNode(getKind(returnType), functionPointerNode, arguments));

            if (fixedWithNext == null) {
                g.start().setNext(callNode);
            } else {
                fixedWithNext.setNext(callNode);
            }

            // box result
            BoxNode boxedResult;
            if (callNode.kind() != Kind.Void) {
                ResolvedJavaType type = getResolvedJavaType(callNode.kind());
                boxedResult = new BoxNode(callNode, type, callNode.kind());
            } else {
                boxedResult = new BoxNode(ConstantNode.forLong(0, g), longJavaType, Kind.Long);
            }

            // box result:
            BoxNode boxNode = g.unique(boxedResult);

            ReturnNode returnNode = g.add(new ReturnNode(boxNode));
            callNode.setNext(returnNode);
            (new WordTypeRewriterPhase(providers.getMetaAccess(), Kind.Long)).apply(g);
            return g;
        } catch (NoSuchMethodException | SecurityException e) {
            throw GraalInternalError.shouldNotReachHere("Call Stub method not found");
        }
    }

    private static FixedWithNextNode getParameters(StructuredGraph g, ParameterNode argumentsArray, int numArgs, Class[] argumentClass, List<ValueNode> args, HotSpotProviders providers) {
        assert numArgs == argumentClass.length;
        FixedWithNextNode fixedWithNext = null;
        for (int i = 0; i < numArgs; i++) {
            // load boxed array element:
            LoadIndexedNode boxedElement = g.add(new LoadIndexedNode(argumentsArray, ConstantNode.forInt(i, g), Kind.Object));
            if (i == 0) {
                g.start().setNext(boxedElement);
                fixedWithNext = boxedElement;
            } else {
                fixedWithNext.setNext(boxedElement);
                fixedWithNext = boxedElement;
            }
            if (getKind(argumentClass[i]) == Kind.Object) {
                // array value
                Kind arrayElementKind = getArrayValuesKind(argumentClass[i]);
                LocationIdentity locationIdentity = NamedLocationIdentity.getArrayLocation(arrayElementKind);
                IndexedLocationNode locationNode = IndexedLocationNode.create(locationIdentity, arrayElementKind, HotSpotGraalRuntime.getArrayBaseOffset(arrayElementKind), ConstantNode.forInt(0, g),
                                g, HotSpotGraalRuntime.getArrayIndexScale(arrayElementKind));
                ComputeAddressNode arrayAddress = g.unique(new ComputeAddressNode(boxedElement, locationNode, StampFactory.forKind(providers.getCodeCache().getTarget().wordKind)));
                args.add(arrayAddress);
            } else {
                // boxed primitive value
                LoadFieldNode loadFieldNode = g.add(new LoadFieldNode(boxedElement, getResolvedJavaField(argumentClass[i])));
                fixedWithNext.setNext(loadFieldNode);
                fixedWithNext = loadFieldNode;
                args.add(loadFieldNode);
            }
        }
        return fixedWithNext;
    }

    public static int getArrayValuesObjectOffset(Class clazz) {
        if (clazz == int[].class) {
            return Unsafe.ARRAY_INT_BASE_OFFSET;
        } else if (clazz == long[].class) {
            return Unsafe.ARRAY_LONG_BASE_OFFSET;
        } else if (clazz == char[].class) {
            return Unsafe.ARRAY_CHAR_BASE_OFFSET;
        } else if (clazz == byte[].class) {
            return Unsafe.ARRAY_BYTE_BASE_OFFSET;
        } else if (clazz == float[].class) {
            return Unsafe.ARRAY_FLOAT_BASE_OFFSET;
        } else if (clazz == double[].class) {
            return Unsafe.ARRAY_DOUBLE_BASE_OFFSET;
        } else {
            throw new IllegalArgumentException("Array Type not supported: " + clazz);
        }
    }

    public static Kind getArrayValuesKind(Class clazz) {
        if (clazz == int[].class) {
            return Kind.Int;
        } else if (clazz == long[].class) {
            return Kind.Long;
        } else if (clazz == char[].class) {
            return Kind.Char;
        } else if (clazz == byte[].class) {
            return Kind.Byte;
        } else if (clazz == float[].class) {
            return Kind.Float;
        } else if (clazz == double[].class) {
            return Kind.Double;
        } else {
            throw new IllegalArgumentException("Array Type not supported: " + clazz);
        }
    }

    private static Kind getKind(Class clazz) {
        if (clazz == int.class || clazz == Integer.class) {
            return Kind.Int;
        } else if (clazz == long.class || clazz == Long.class) {
            return Kind.Long;
        } else if (clazz == char.class || clazz == Character.class) {
            return Kind.Char;
        } else if (clazz == byte.class || clazz == Byte.class) {
            return Kind.Byte;
        } else if (clazz == float.class || clazz == Float.class) {
            return Kind.Float;
        } else if (clazz == double.class || clazz == Double.class) {
            return Kind.Double;
        } else if (clazz == int[].class || clazz == long[].class || clazz == char[].class || clazz == byte[].class || clazz == float[].class || clazz == double[].class) {
            return Kind.Object;
        } else if (clazz == void.class) {
            return Kind.Void;
        } else {
            throw new IllegalArgumentException("Type not supported: " + clazz);
        }
    }

    private static ResolvedJavaField getResolvedJavaField(Class containerClass) {
        if (containerClass == int.class || containerClass == Integer.class) {
            return integerValueField;
        } else if (containerClass == long.class || containerClass == Long.class) {
            return longValueField;
        } else if (containerClass == char.class || containerClass == Character.class) {
            return charValueField;
        } else if (containerClass == byte.class || containerClass == Byte.class) {
            return byteValueField;
        } else if (containerClass == float.class || containerClass == Float.class) {
            return floatValueField;
        } else if (containerClass == double.class || containerClass == Double.class) {
            return doubleValueField;
        } else {
            throw new IllegalArgumentException("Type not supported: " + containerClass);
        }
    }

    private static ResolvedJavaType getResolvedJavaType(Kind kind) {
        if (kind == Kind.Int) {
            return integerJavaType;
        } else if (kind == Kind.Long) {
            return longJavaType;
        } else if (kind == Kind.Char) {
            return charJavaType;
        } else if (kind == Kind.Byte) {
            return byteJavaType;
        } else if (kind == Kind.Float) {
            return floatJavaType;
        } else if (kind == Kind.Double) {
            return doubleJavaType;
        } else {
            throw GraalInternalError.shouldNotReachHere();
        }
    }

    @SuppressWarnings("unused")
    public static Object libCall(Object argLoc, Object unused1, Object unused2) {
        throw GraalInternalError.shouldNotReachHere("GNFI Callstub method must not be called!");
    }
}