/*
* 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.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.llvm.runtime.global.LLVMGlobalVariableRegistry;
import com.oracle.truffle.llvm.runtime.memory.LLVMMemory;
import com.oracle.truffle.llvm.runtime.memory.LLVMNativeFunctions;
import com.oracle.truffle.llvm.runtime.memory.LLVMThreadingStack;
import com.oracle.truffle.llvm.runtime.options.LLVMOptions;
import com.oracle.truffle.llvm.runtime.types.FunctionType;
import com.oracle.truffle.llvm.runtime.types.MetaType;
import com.oracle.truffle.llvm.runtime.types.Type;
public class LLVMContext {
private final List<RootCallTarget> globalVarInits = new ArrayList<>();
private final List<RootCallTarget> globalVarDeallocs = new ArrayList<>();
private final List<RootCallTarget> constructorFunctions = new ArrayList<>();
private final List<RootCallTarget> destructorFunctions = new ArrayList<>();
private final Deque<LLVMFunctionDescriptor> atExitFunctions = new ArrayDeque<>();
private final List<LLVMThread> runningThreads = new ArrayList<>();
private final LLVMGlobalVariableRegistry globalVariableRegistry = new LLVMGlobalVariableRegistry();
private final NativeLookup nativeLookup;
private final LLVMNativeFunctions nativeFunctions;
private final LLVMThreadingStack threadingStack = new LLVMThreadingStack();
private Object[] mainArguments;
private Source mainSourceFile;
private boolean parseOnly;
private boolean haveLoadedDynamicBitcodeLibraries;
private int currentFunctionIndex = 0;
private final List<LLVMFunctionDescriptor> functionDescriptors = new ArrayList<>();
private final HashMap<String, LLVMFunctionDescriptor> llvmIRFunctions;
private NativeIntrinsicProvider nativeIntrinsicsFactory;
private final LinkedList<LLVMAddress> caughtExceptionStack = new LinkedList<>();
private final LinkedList<DestructorStackElement> destructorStack = new LinkedList<>();
private final HashMap<String, Integer> nativeCallStatistics;
private final Object handlesLock;
private final IdentityHashMap<TruffleObject, LLVMAddress> toNative;
private final HashMap<LLVMAddress, TruffleObject> toManaged;
// #define SIG_DFL ((__sighandler_t) 0) /* Default action. */
private final LLVMFunction sigDfl;
// # define SIG_IGN ((__sighandler_t) 1) /* Ignore signal. */
private final LLVMFunction sigIgn;
// #define SIG_ERR ((__sighandler_t) -1) /* Error return. */
private final LLVMFunction sigErr;
private static final String ZERO_FUNCTION = "<zero function>";
public static final class DestructorStackElement {
private final LLVMFunctionDescriptor destructor;
private final LLVMAddress thiz;
public DestructorStackElement(LLVMFunctionDescriptor destructor, LLVMAddress thiz) {
this.destructor = destructor;
this.thiz = thiz;
}
public LLVMFunctionDescriptor getDestructor() {
return destructor;
}
public LLVMAddress getThiz() {
return thiz;
}
}
public LLVMContext(Env env) {
this.nativeLookup = LLVMOptions.ENGINE.disableNativeInterface() ? null : new NativeLookup(env);
this.nativeCallStatistics = !LLVMLogger.TARGET_NONE.equals(LLVMOptions.DEBUG.printNativeCallStatistics()) ? new HashMap<>() : null;
this.llvmIRFunctions = new HashMap<>();
this.nativeFunctions = new LLVMNativeFunctionsImpl(nativeLookup);
this.sigDfl = LLVMFunctionHandle.createHandle(0);
this.sigIgn = LLVMFunctionHandle.createHandle(1);
this.sigErr = LLVMFunctionHandle.createHandle((-1) & LLVMFunction.LOWER_MASK);
this.toNative = new IdentityHashMap<>();
this.toManaged = new HashMap<>();
this.handlesLock = new Object();
assert currentFunctionIndex == 0;
LLVMFunctionDescriptor zeroFunction = LLVMFunctionDescriptor.createDescriptor(this, ZERO_FUNCTION, new FunctionType(MetaType.UNKNOWN, new Type[0], false), currentFunctionIndex++);
this.llvmIRFunctions.put(ZERO_FUNCTION, zeroFunction);
this.functionDescriptors.add(zeroFunction);
}
public void setNativeIntrinsicsFactory(NativeIntrinsicProvider nativeIntrinsicsFactory) {
this.nativeIntrinsicsFactory = nativeIntrinsicsFactory;
}
public NativeIntrinsicProvider getNativeIntrinsicsProvider() {
return nativeIntrinsicsFactory;
}
public LLVMFunction getSigDfl() {
return sigDfl;
}
public LLVMFunction getSigIgn() {
return sigIgn;
}
public LLVMFunction getSigErr() {
return sigErr;
}
@TruffleBoundary
public TruffleObject getManagedObjectForHandle(LLVMAddress address) {
synchronized (handlesLock) {
final TruffleObject object = toManaged.get(address);
if (object == null) {
throw new UnsupportedOperationException("Cannot resolve native handle: " + address);
}
return object;
}
}
@TruffleBoundary
public void releaseHandle(LLVMAddress address) {
synchronized (handlesLock) {
final TruffleObject object = toManaged.get(address);
if (object == null) {
throw new UnsupportedOperationException("Cannot resolve native handle: " + address);
}
toManaged.remove(address);
toNative.remove(object);
LLVMMemory.free(address);
}
}
@TruffleBoundary
public LLVMAddress getHandleForManagedObject(TruffleObject object) {
synchronized (handlesLock) {
return toNative.computeIfAbsent(object, (k) -> {
LLVMAddress allocatedMemory = LLVMMemory.allocateMemory(Long.BYTES);
LLVMMemory.putI64(allocatedMemory, 0xdeadbeef);
toManaged.put(allocatedMemory, object);
return allocatedMemory;
});
}
}
@TruffleBoundary
public void registerNativeCall(LLVMFunctionDescriptor descriptor) {
if (nativeCallStatistics != null) {
String name = descriptor.getName() + " " + descriptor.getType();
if (nativeCallStatistics.containsKey(name)) {
int count = nativeCallStatistics.get(name) + 1;
nativeCallStatistics.put(name, count);
} else {
nativeCallStatistics.put(name, 1);
}
}
}
public void printNativeCallStatistic() {
if (nativeCallStatistics != null) {
LinkedHashMap<String, Integer> sorted = nativeCallStatistics.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new));
for (String s : sorted.keySet()) {
System.err.println(String.format("Function %s \t count: %d", s, sorted.get(s)));
}
}
}
public LLVMNativeFunctions getNativeFunctions() {
return nativeFunctions;
}
public LinkedList<LLVMAddress> getCaughtExceptionStack() {
return caughtExceptionStack;
}
public LinkedList<DestructorStackElement> getDestructorStack() {
return destructorStack;
}
public LLVMGlobalVariableRegistry getGlobalVariableRegistry() {
return globalVariableRegistry;
}
public void addLibraryToNativeLookup(String library) {
nativeLookup.addLibraryToNativeLookup(library);
}
public LLVMThreadingStack getThreadingStack() {
return threadingStack;
}
public void setMainArguments(Object[] mainArguments) {
this.mainArguments = mainArguments;
}
public Object[] getMainArguments() {
return mainArguments;
}
public void setMainSourceFile(Source mainSourceFile) {
this.mainSourceFile = mainSourceFile;
}
public Source getMainSourceFile() {
return mainSourceFile;
}
public void registerGlobalVarDealloc(RootCallTarget globalVarDealloc) {
globalVarDeallocs.add(globalVarDealloc);
}
public void registerConstructorFunction(RootCallTarget constructorFunction) {
constructorFunctions.add(constructorFunction);
}
public void registerDestructorFunction(RootCallTarget destructorFunction) {
destructorFunctions.add(destructorFunction);
}
public void registerAtExitFunction(LLVMFunctionDescriptor atExitFunction) {
atExitFunctions.push(atExitFunction);
}
public void registerGlobalVarInit(RootCallTarget globalVarInit) {
globalVarInits.add(globalVarInit);
}
public synchronized void registerThread(LLVMThread thread) {
assert !runningThreads.contains(thread);
runningThreads.add(thread);
}
public synchronized void unregisterThread(LLVMThread thread) {
runningThreads.remove(thread);
assert !runningThreads.contains(thread);
}
@TruffleBoundary
public synchronized void shutdownThreads() {
// we need to iterate over a copy of the list, because stop() can modify the original list
for (LLVMThread node : new ArrayList<>(runningThreads)) {
node.stop();
}
}
@TruffleBoundary
public synchronized void awaitThreadTermination() {
shutdownThreads();
while (!runningThreads.isEmpty()) {
LLVMThread node = runningThreads.get(0);
node.awaitFinish();
assert !runningThreads.contains(node); // should be unregistered by LLVMThreadNode
}
}
public List<RootCallTarget> getGlobalVarDeallocs() {
return globalVarDeallocs;
}
public List<RootCallTarget> getConstructorFunctions() {
return constructorFunctions;
}
public List<RootCallTarget> getDestructorFunctions() {
return destructorFunctions;
}
public Deque<LLVMFunctionDescriptor> getAtExitFunctions() {
return atExitFunctions;
}
public List<RootCallTarget> getGlobalVarInits() {
return globalVarInits;
}
public synchronized List<LLVMThread> getRunningThreads() {
return Collections.unmodifiableList(runningThreads);
}
public void setParseOnly(boolean parseOnly) {
this.parseOnly = parseOnly;
}
public boolean isParseOnly() {
return parseOnly;
}
public boolean haveLoadedDynamicBitcodeLibraries() {
return haveLoadedDynamicBitcodeLibraries;
}
public void setHaveLoadedDynamicBitcodeLibraries() {
haveLoadedDynamicBitcodeLibraries = true;
}
public LLVMFunctionDescriptor lookup(LLVMFunctionHandle handle) {
assert handle.isSulong();
return functionDescriptors.get(handle.getSulongFunctionIndex());
}
public List<LLVMFunctionDescriptor> getFunctionDescriptors() {
return functionDescriptors;
}
public LLVMFunctionDescriptor getZeroFunctionDescriptor() {
return functionDescriptors.get(0);
}
public interface FunctionFactory {
LLVMFunctionDescriptor create(int index);
}
public LLVMFunctionDescriptor lookupFunctionDescriptor(String name, FunctionFactory factory) {
LLVMFunctionDescriptor function = llvmIRFunctions.get(name);
if (function == null) {
function = factory.create(currentFunctionIndex++);
functionDescriptors.add(function);
llvmIRFunctions.put(name, function);
assert LLVMFunction.getSulongFunctionIndex(function.getFunctionPointer()) == currentFunctionIndex - 1;
assert functionDescriptors.get(currentFunctionIndex - 1) == function;
}
return function;
}
public NativeLookup getNativeLookup() {
return nativeLookup;
}
public static String getNativeSignature(FunctionType type) {
return NativeLookup.prepareSignature(type);
}
}