/* * Copyright (c) 2014, 2016, 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 org.graalvm.compiler.truffle.hotspot.nfi; import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider.getArrayBaseOffset; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.core.GraalCompiler; import org.graalvm.compiler.core.target.Backend; import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.Debug.Scope; import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder; import org.graalvm.compiler.hotspot.meta.HotSpotProviders; import org.graalvm.compiler.java.GraphBuilderPhase; import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; import org.graalvm.compiler.lir.phases.LIRSuites; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.BoxNode; import org.graalvm.compiler.nodes.extended.UnboxNode; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.LateRegistration; import org.graalvm.compiler.nodes.java.LoadIndexedNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.PhaseSuite; import org.graalvm.compiler.phases.tiers.HighTierContext; import org.graalvm.compiler.phases.tiers.Suites; import org.graalvm.compiler.replacements.ConstantBindingParameterPlugin; import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; import jdk.vm.ci.hotspot.HotSpotCompiledCode; import jdk.vm.ci.meta.DefaultProfilingInfo; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.TriState; /** * Utility creating a graph for a stub used to call a native function. */ public class NativeCallStubGraphBuilder { private final OptionValues options; private final HotSpotProviders providers; private final Backend backend; private final RawNativeCallNodeFactory factory; private final ResolvedJavaMethod callStubMethod; private class CallPlugin implements InvocationPlugin { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) { JavaConstant constHandle = receiver.get().asJavaConstant(); if (constHandle != null) { HotSpotNativeFunctionHandle handle = providers.getSnippetReflection().asObject(HotSpotNativeFunctionHandle.class, constHandle); buildNativeCall(b, handle.getPointer(), arg, handle.getReturnType(), handle.getArgumentTypes()); return true; } else { return false; } } } private static class NativeCallStub { @SuppressWarnings("unused") private static Object libCall(HotSpotNativeFunctionHandle function, Object[] args) { return function.call(args); } } NativeCallStubGraphBuilder(OptionValues options, HotSpotProviders providers, Backend backend, RawNativeCallNodeFactory factory) { this.options = options; this.providers = providers; this.backend = backend; this.factory = factory; InvocationPlugins plugins = providers.getGraphBuilderPlugins().getInvocationPlugins(); try (LateRegistration r = new LateRegistration(plugins, HotSpotNativeFunctionHandle.class)) { r.register(new CallPlugin(), "call", Receiver.class, Object[].class); } ResolvedJavaType stubClass = providers.getMetaAccess().lookupJavaType(NativeCallStub.class); ResolvedJavaMethod[] methods = stubClass.getDeclaredMethods(); assert methods.length == 1 && methods[0].getName().equals("libCall"); this.callStubMethod = methods[0]; } /** * Creates and installs a stub for calling a native function. */ @SuppressWarnings("try") void installNativeFunctionStub(HotSpotNativeFunctionHandle function) { Plugins plugins = new Plugins(providers.getGraphBuilderPlugins()); plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(new Object[]{function, null}, providers.getMetaAccess(), providers.getSnippetReflection())); PhaseSuite<HighTierContext> graphBuilder = new PhaseSuite<>(); graphBuilder.appendPhase(new GraphBuilderPhase(GraphBuilderConfiguration.getDefault(plugins))); Suites suites = providers.getSuites().getDefaultSuites(options); LIRSuites lirSuites = providers.getSuites().getDefaultLIRSuites(options); StructuredGraph g = new StructuredGraph.Builder(options).method(callStubMethod).compilationId(backend.getCompilationIdentifier(callStubMethod)).build(); CompilationResult compResult = GraalCompiler.compileGraph(g, callStubMethod, providers, backend, graphBuilder, OptimisticOptimizations.ALL, DefaultProfilingInfo.get(TriState.UNKNOWN), suites, lirSuites, new CompilationResult(), CompilationResultBuilderFactory.Default); HotSpotCodeCacheProvider codeCache = providers.getCodeCache(); try (Scope s = Debug.scope("CodeInstall", codeCache, g.method(), compResult)) { HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, g.method(), null, compResult); function.code = codeCache.addCode(g.method(), compiledCode, null, null); } catch (Throwable e) { throw Debug.handle(e); } } private void buildNativeCall(GraphBuilderContext b, HotSpotNativeFunctionPointer functionPointer, ValueNode argArray, Class<?> returnType, Class<?>... argumentTypes) { JavaConstant functionPointerNode = JavaConstant.forLong(functionPointer.getRawValue()); ValueNode[] arguments = getParameters(b, argArray, argumentTypes); FixedWithNextNode callNode = b.add(factory.createRawCallNode(getKind(returnType), functionPointerNode, arguments)); // box result if (callNode.getStackKind() != JavaKind.Void) { if (callNode.getStackKind() == JavaKind.Object) { throw new IllegalArgumentException("Return type not supported: " + returnType.getName()); } ResolvedJavaType type = b.getMetaAccess().lookupJavaType(callNode.getStackKind().toBoxedJavaClass()); b.addPush(JavaKind.Object, new BoxNode(callNode, type, callNode.getStackKind())); } else { ValueNode zero = b.add(ConstantNode.forLong(0)); b.addPush(JavaKind.Object, new BoxNode(zero, b.getMetaAccess().lookupJavaType(Long.class), JavaKind.Long)); } } private static ValueNode[] getParameters(GraphBuilderContext b, ValueNode argArray, Class<?>[] argumentTypes) { ValueNode[] ret = new ValueNode[argumentTypes.length]; for (int i = 0; i < argumentTypes.length; i++) { // load boxed array element: ValueNode idx = b.add(ConstantNode.forInt(i)); Class<?> type = argumentTypes[i]; JavaKind kind = getKind(type); LoadIndexedNode boxedElement = b.add(new LoadIndexedNode(b.getAssumptions(), argArray, idx, JavaKind.Object)); if (kind == JavaKind.Object) { // array value JavaKind arrayElementKind = getElementKind(type); int displacement = getArrayBaseOffset(arrayElementKind); ValueNode dispNode = b.add(ConstantNode.forLong(displacement)); ret[i] = b.add(new OffsetAddressNode(boxedElement, dispNode)); } else { // boxed primitive value ret[i] = b.add(new UnboxNode(boxedElement, kind)); } } return ret; } public static JavaKind getElementKind(Class<?> clazz) { Class<?> componentType = clazz.getComponentType(); if (componentType == null) { throw new IllegalArgumentException("Parameter type not supported: " + clazz); } if (componentType.isPrimitive()) { return JavaKind.fromJavaClass(componentType); } throw new IllegalArgumentException("Parameter type not supported: " + clazz); } private static JavaKind getKind(Class<?> clazz) { if (clazz == int.class || clazz == Integer.class) { return JavaKind.Int; } else if (clazz == long.class || clazz == Long.class) { return JavaKind.Long; } else if (clazz == char.class || clazz == Character.class) { return JavaKind.Char; } else if (clazz == byte.class || clazz == Byte.class) { return JavaKind.Byte; } else if (clazz == float.class || clazz == Float.class) { return JavaKind.Float; } else if (clazz == double.class || clazz == Double.class) { return JavaKind.Double; } else if (clazz == int[].class || clazz == long[].class || clazz == char[].class || clazz == byte[].class || clazz == float[].class || clazz == double[].class) { return JavaKind.Object; } else if (clazz == void.class) { return JavaKind.Void; } else { throw new IllegalArgumentException("Type not supported: " + clazz); } } }