/*
* Copyright (c) 2017, Oracle and/or its affiliates.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.truffle.llvm.nodes.func;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMFunctionDescriptor;
import com.oracle.truffle.llvm.runtime.NativeLookup;
import com.oracle.truffle.llvm.runtime.LLVMFunctionDescriptor.Intrinsic;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMNode;
import com.oracle.truffle.llvm.runtime.types.FunctionType;
@SuppressWarnings("unused")
public abstract class LLVMDispatchNode extends LLVMNode {
protected static final int INLINE_CACHE_SIZE = 5;
private final FunctionType type;
@CompilationFinal private String signature;
protected LLVMDispatchNode(FunctionType type) {
this.type = type;
}
private String getSignature() {
if (signature == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.signature = LLVMContext.getNativeSignature(type);
}
return signature;
}
public abstract Object executeDispatch(VirtualFrame frame, LLVMFunctionDescriptor function, Object[] arguments);
/*
* Function is defined in the user program (available as LLVM IR)
*/
@Specialization(limit = "INLINE_CACHE_SIZE", guards = {"function.getFunctionPointer() == cachedFunction.getFunctionPointer()", "cachedFunction.isLLVMIRFunction()"})
protected static Object doDirect(LLVMFunctionDescriptor function, Object[] arguments,
@Cached("function") LLVMFunctionDescriptor cachedFunction,
@Cached("create(cachedFunction.getLLVMIRFunction())") DirectCallNode callNode) {
return callNode.call(arguments);
}
@Specialization(replaces = "doDirect", guards = "descriptor.isLLVMIRFunction()")
protected static Object doIndirect(LLVMFunctionDescriptor descriptor, Object[] arguments,
@Cached("create()") IndirectCallNode callNode) {
return callNode.call(descriptor.getLLVMIRFunction(), arguments);
}
/*
* Function is not defined in the user program (not available as LLVM IR). This would normally
* result in a native call BUT there is an intrinsification available
*/
protected DirectCallNode getIntrinsificationCallNode(Intrinsic intrinsic) {
RootCallTarget target = intrinsic.forceSplit() ? intrinsic.generateCallTarget(type) : intrinsic.cachedCallTarget(type);
DirectCallNode directCallNode = DirectCallNode.create(target);
if (intrinsic.forceInline()) {
directCallNode.forceInlining();
}
return directCallNode;
}
@Specialization(limit = "INLINE_CACHE_SIZE", guards = {"function.getFunctionPointer() == cachedFunction.getFunctionPointer()", "cachedFunction.isNativeIntrinsicFunction()"})
protected Object doDirectIntrinsic(LLVMFunctionDescriptor function, Object[] arguments,
@Cached("function") LLVMFunctionDescriptor cachedFunction,
@Cached("getIntrinsificationCallNode(cachedFunction.getNativeIntrinsic())") DirectCallNode callNode) {
return callNode.call(arguments);
}
@Specialization(replaces = "doDirectIntrinsic", guards = "descriptor.isNativeIntrinsicFunction()")
protected Object doIndirectIntrinsic(LLVMFunctionDescriptor descriptor, Object[] arguments,
@Cached("create()") IndirectCallNode callNode) {
return callNode.call(descriptor.getNativeIntrinsic().cachedCallTarget(type), arguments);
}
/*
* Function is not defined in the user program (not available as LLVM IR). No intrinsic
* available. We do a native call.
*/
@Specialization(limit = "10", guards = {"descriptor.getFunctionPointer() == cachedDescriptor.getFunctionPointer()", "descriptor.isNativeFunction()"})
protected Object doCachedNative(VirtualFrame frame, LLVMFunctionDescriptor descriptor, Object[] arguments,
@Cached("descriptor") LLVMFunctionDescriptor cachedDescriptor,
@Cached("createToNativeNodes()") LLVMNativeConvertNode[] toNative,
@Cached("createFromNativeNode()") LLVMNativeConvertNode fromNative,
@Cached("createNativeCallNode()") Node nativeCall,
@Cached("bindSymbol(frame, cachedDescriptor)") TruffleObject cachedBoundFunction,
@Cached("getContext()") LLVMContext context) {
Object[] nativeArgs = prepareNativeArguments(frame, arguments, toNative);
Object returnValue = LLVMNativeCallUtils.callNativeFunction(context, nativeCall, cachedBoundFunction, nativeArgs, cachedDescriptor);
return fromNative.executeConvert(frame, returnValue);
}
protected TruffleObject bindSymbol(VirtualFrame frame, LLVMFunctionDescriptor descriptor) {
CompilerAsserts.neverPartOfCompilation();
return LLVMNativeCallUtils.bindNativeSymbol(LLVMNativeCallUtils.getBindNode(), descriptor.getNativeFunction(), getSignature());
}
@Specialization(replaces = "doCachedNative", guards = "descriptor.isNativeFunction()")
protected Object doNative(VirtualFrame frame, LLVMFunctionDescriptor descriptor, Object[] arguments,
@Cached("createToNativeNodes()") LLVMNativeConvertNode[] toNative,
@Cached("createFromNativeNode()") LLVMNativeConvertNode fromNative,
@Cached("createNativeCallNode()") Node nativeCall,
@Cached("getBindNode()") Node bindNode) {
Object[] nativeArgs = prepareNativeArguments(frame, arguments, toNative);
TruffleObject boundSymbol = LLVMNativeCallUtils.bindNativeSymbol(bindNode, descriptor.getNativeFunction(), getSignature());
Object returnValue = LLVMNativeCallUtils.callNativeFunction(getContext(), nativeCall, boundSymbol, nativeArgs, descriptor);
return fromNative.executeConvert(frame, returnValue);
}
@ExplodeLoop
private static Object[] prepareNativeArguments(VirtualFrame frame, Object[] arguments, LLVMNativeConvertNode[] toNative) {
Object[] nativeArgs = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
nativeArgs[i] = toNative[i].executeConvert(frame, arguments[i]);
}
return nativeArgs;
}
protected Node getBindNode() {
CompilerAsserts.neverPartOfCompilation();
return LLVMNativeCallUtils.getBindNode();
}
protected Node createNativeCallNode() {
CompilerAsserts.neverPartOfCompilation();
int argCount = type.getArgumentTypes().length;
return Message.createExecute(argCount).createNode();
}
@ExplodeLoop
protected LLVMNativeConvertNode[] createToNativeNodes() {
LLVMNativeConvertNode[] ret = new LLVMNativeConvertNode[type.getArgumentTypes().length];
for (int i = 0; i < type.getArgumentTypes().length; i++) {
ret[i] = LLVMNativeConvertNode.createToNative(type.getArgumentTypes()[i]);
}
return ret;
}
protected LLVMNativeConvertNode createFromNativeNode() {
CompilerAsserts.neverPartOfCompilation();
return LLVMNativeConvertNode.createFromNative(type.getReturnType());
}
}