/*
* 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.nodes.func;
import java.util.Deque;
import com.oracle.truffle.api.CallTarget;
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.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.llvm.nodes.intrinsics.c.LLVMAbort;
import com.oracle.truffle.llvm.nodes.intrinsics.c.LLVMSignal;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMContext.DestructorStackElement;
import com.oracle.truffle.llvm.runtime.LLVMExitException;
import com.oracle.truffle.llvm.runtime.LLVMFunctionDescriptor;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.LLVMTruffleAddress;
import com.oracle.truffle.llvm.runtime.SulongRuntimeException;
import com.oracle.truffle.llvm.runtime.types.PointerType;
/**
* The global entry point initializes the global scope and starts execution with the main function.
* This class might be subclassed by other projects.
*/
public class LLVMGlobalRootNode extends RootNode {
@Child private Node executeDestructor = Message.createExecute(1).createNode();
private final DirectCallNode main;
@CompilationFinal(dimensions = 1) protected final Object[] arguments;
public LLVMGlobalRootNode(LLVMLanguage language, FrameDescriptor descriptor, CallTarget main, Object... arguments) {
super(language, descriptor);
this.main = Truffle.getRuntime().createDirectCallNode(main);
this.arguments = arguments;
}
@Override
@ExplodeLoop
public Object execute(VirtualFrame frame) {
try {
Object result = null;
assert LLVMSignal.getNumberOfRegisteredSignals() == 0;
result = executeIteration(arguments);
getContext().awaitThreadTermination();
assert LLVMSignal.getNumberOfRegisteredSignals() == 0;
return result;
} catch (LLVMExitException e) {
getContext().awaitThreadTermination();
assert LLVMSignal.getNumberOfRegisteredSignals() == 0;
return e.getReturnCode();
} catch (SulongRuntimeException e) {
System.err.println();
e.getCStackTrace().printCStackTrace();
throw e;
} finally {
runDestructors();
// if not done already, we want at least call a shutdown command
getContext().shutdownThreads();
}
}
@TruffleBoundary
private void runDestructors() {
for (DestructorStackElement destructorStackElement : getContext().getDestructorStack()) {
try {
ForeignAccess.sendExecute(executeDestructor, destructorStackElement.getDestructor(),
new LLVMTruffleAddress(destructorStackElement.getThiz(), new PointerType(null), getContext()));
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException(e);
}
}
}
protected Object executeIteration(Object[] args) {
Object result;
int returnCode = 0;
try {
result = main.call(args);
} catch (LLVMExitException e) {
returnCode = e.getReturnCode();
throw e;
} finally {
// We shouldn't execute atexit, when there was an abort
if (returnCode != LLVMAbort.UNIX_SIGABORT) {
executeAtExitFunctions();
}
}
return result;
}
@TruffleBoundary
private void executeAtExitFunctions() {
Deque<LLVMFunctionDescriptor> atExitFunctions = getContext().getAtExitFunctions();
LLVMExitException lastExitException = null;
while (!atExitFunctions.isEmpty()) {
try {
try {
ForeignAccess.sendExecute(Message.createExecute(0).createNode(), atExitFunctions.pop());
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
throw new IllegalStateException(e);
}
} catch (LLVMExitException e) {
lastExitException = e;
}
}
if (lastExitException != null) {
throw lastExitException;
}
}
public final LLVMContext getContext() {
return getRootNode().getLanguage(LLVMLanguage.class).getContextReference().get();
}
}