/*
* Copyright (c) 2016, 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.runtime;
import java.util.HashMap;
import java.util.Map;
import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.llvm.runtime.interop.LLVMFunctionMessageResolutionForeign;
import com.oracle.truffle.llvm.runtime.types.FunctionType;
public final class LLVMFunctionDescriptor implements LLVMFunction, TruffleObject, Comparable<LLVMFunctionDescriptor> {
private final String functionName;
private final FunctionType type;
private final long functionId;
private final LLVMContext context;
@CompilationFinal private Function function;
@CompilationFinal private Assumption functionAssumption;
public static final class Intrinsic {
private final String name;
private final Map<FunctionType, RootCallTarget> overloadingMap;
private final NativeIntrinsicProvider provider;
private final boolean forceInline;
private final boolean forceSplit;
public Intrinsic(NativeIntrinsicProvider provider, String name) {
this.name = name;
this.overloadingMap = new HashMap<>();
this.provider = provider;
this.forceInline = provider.forceInline(name);
this.forceSplit = provider.forceSplit(name);
}
public boolean forceInline() {
return forceInline;
}
public boolean forceSplit() {
return forceSplit;
}
public RootCallTarget generateCallTarget(FunctionType type) {
return generate(type);
}
public RootCallTarget cachedCallTarget(FunctionType type) {
if (exists(type)) {
return get(type);
} else {
return generate(type);
}
}
@TruffleBoundary
private boolean exists(FunctionType type) {
return overloadingMap.containsKey(type);
}
@TruffleBoundary
private RootCallTarget get(FunctionType type) {
return overloadingMap.get(type);
}
private RootCallTarget generate(FunctionType type) {
CompilerDirectives.transferToInterpreterAndInvalidate();
RootCallTarget newTarget = provider.generateIntrinsic(name, type);
assert newTarget != null;
overloadingMap.put(type, newTarget);
return newTarget;
}
}
abstract static class Function {
void resolve(@SuppressWarnings("unused") LLVMFunctionDescriptor descriptor) {
// nothing to do
}
}
static final class LazyLLVMIRFunction extends Function {
private final LazyToTruffleConverter converter;
LazyLLVMIRFunction(LazyToTruffleConverter converter) {
this.converter = converter;
}
@Override
void resolve(LLVMFunctionDescriptor descriptor) {
descriptor.setFunction(new LLVMIRFunction(converter.convert()));
}
}
static final class LLVMIRFunction extends Function {
private final RootCallTarget callTarget;
LLVMIRFunction(RootCallTarget callTarget) {
this.callTarget = callTarget;
}
}
static final class UnresolvedFunction extends Function {
@Override
void resolve(LLVMFunctionDescriptor descriptor) {
if (descriptor.context.getNativeIntrinsicsProvider().isIntrinsified(descriptor.functionName)) {
Intrinsic intrinsification = new Intrinsic(descriptor.context.getNativeIntrinsicsProvider(), descriptor.functionName);
descriptor.setFunction(new NativeIntrinsicFunction(intrinsification));
} else {
TruffleObject nativeFunction;
if (!descriptor.isNullFunction()) {
nativeFunction = descriptor.context.getNativeLookup().getNativeFunction(descriptor.getName());
} else {
nativeFunction = null;
}
descriptor.setFunction(new NativeFunction(nativeFunction));
}
}
}
static final class NativeIntrinsicFunction extends Function {
private final Intrinsic intrinsic;
NativeIntrinsicFunction(Intrinsic intrinsic) {
this.intrinsic = intrinsic;
}
}
static final class NativeFunction extends Function {
private final TruffleObject nativeFunction;
NativeFunction(TruffleObject nativeFunction) {
this.nativeFunction = nativeFunction;
}
}
private void setFunction(Function newFunction) {
CompilerDirectives.transferToInterpreterAndInvalidate();
functionAssumption.invalidate();
this.function = newFunction;
this.functionAssumption = Truffle.getRuntime().createAssumption();
}
private Function getFunction() {
if (!functionAssumption.isValid()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
}
return function;
}
private LLVMFunctionDescriptor(LLVMContext context, String name, FunctionType type, long functionId) {
CompilerAsserts.neverPartOfCompilation();
assert LLVMFunction.isSulongFunctionPointer(functionId);
this.context = context;
this.functionName = name;
this.type = type;
this.functionId = functionId;
this.functionAssumption = Truffle.getRuntime().createAssumption();
this.function = new UnresolvedFunction();
}
public static LLVMFunctionDescriptor createDescriptor(LLVMContext context, String name, FunctionType type, long functionId) {
assert (functionId & LLVMFunction.UPPER_MASK) == 0;
LLVMFunctionDescriptor func = new LLVMFunctionDescriptor(context, name, type, LLVMFunction.tagSulongFunctionPointer(functionId));
return func;
}
public interface LazyToTruffleConverter {
RootCallTarget convert();
}
public boolean isLLVMIRFunction() {
return getFunction() instanceof LLVMIRFunction || getFunction() instanceof LazyLLVMIRFunction;
}
public boolean isNativeIntrinsicFunction() {
getFunction().resolve(this);
return getFunction() instanceof NativeIntrinsicFunction;
}
public boolean isNativeFunction() {
getFunction().resolve(this);
return getFunction() instanceof NativeFunction;
}
public void declareInSulong(LazyToTruffleConverter converter) {
setFunction(new LazyLLVMIRFunction(converter));
}
public void declareInSulong(RootCallTarget callTarget) {
setFunction(new LLVMIRFunction(callTarget));
}
public RootCallTarget getLLVMIRFunction() {
getFunction().resolve(this);
assert getFunction() instanceof LLVMIRFunction;
return ((LLVMIRFunction) getFunction()).callTarget;
}
public Intrinsic getNativeIntrinsic() {
getFunction().resolve(this);
assert getFunction() instanceof NativeIntrinsicFunction;
return ((NativeIntrinsicFunction) getFunction()).intrinsic;
}
public TruffleObject getNativeFunction() {
getFunction().resolve(this);
assert getFunction() instanceof NativeFunction;
return ((NativeFunction) getFunction()).nativeFunction;
}
public String getName() {
return functionName;
}
public FunctionType getType() {
return type;
}
/**
* Gets an unique index for a function descriptor.
*
* @return the function's index
*/
@Override
public long getFunctionPointer() {
return functionId;
}
@Override
public boolean isNullFunction() {
return LLVMFunction.getSulongFunctionIndex(functionId) == 0;
}
@Override
public String toString() {
if (functionName != null) {
return String.format("function@%d '%s'", functionId, functionName);
} else {
return String.format("function@%d (anonymous)", functionId);
}
}
@Override
public int compareTo(LLVMFunctionDescriptor o) {
return Long.compare(functionId, o.getFunctionPointer());
}
@Override
public int hashCode() {
return (int) functionId;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof LLVMFunctionDescriptor)) {
return false;
} else {
LLVMFunctionDescriptor other = (LLVMFunctionDescriptor) obj;
return getFunctionPointer() == other.getFunctionPointer();
}
}
public static boolean isInstance(TruffleObject object) {
return object instanceof LLVMFunctionDescriptor;
}
public LLVMContext getContext() {
return context;
}
@Override
public ForeignAccess getForeignAccess() {
return LLVMFunctionMessageResolutionForeign.ACCESS;
}
}