/*
* 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 com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotTypeException;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.llvm.runtime.LLVMAddress;
import com.oracle.truffle.llvm.runtime.LLVMException;
import com.oracle.truffle.llvm.runtime.memory.LLVMMemory;
import com.oracle.truffle.llvm.runtime.memory.LLVMNativeFunctions;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
public final class LLVMLandingpadNode extends LLVMExpressionNode {
@Child private LLVMExpressionNode allocateLandingPadValue;
@Child private LLVMNativeFunctions.SulongGetUnwindHeaderNode getUnwindHeader;
@Child private LLVMNativeFunctions.SulongGetExceptionTypeNode getExceptionType;
@Children private final LandingpadEntryNode[] entries;
private final FrameSlot exceptionSlot;
private final boolean cleanup;
public LLVMLandingpadNode(LLVMExpressionNode allocateLandingPadValue, FrameSlot exceptionSlot, boolean cleanup,
LandingpadEntryNode[] entries) {
this.allocateLandingPadValue = allocateLandingPadValue;
this.exceptionSlot = exceptionSlot;
this.cleanup = cleanup;
this.entries = entries;
}
public LLVMNativeFunctions.SulongGetUnwindHeaderNode getGetUnwindHeader() {
if (getUnwindHeader == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.getUnwindHeader = insert(getContext().getNativeFunctions().createGetUnwindHeader());
}
return getUnwindHeader;
}
public LLVMNativeFunctions.SulongGetExceptionTypeNode getGetExceptionType() {
if (getExceptionType == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.getExceptionType = insert(getContext().getNativeFunctions().createGetExceptionType());
}
return getExceptionType;
}
@Override
public Object executeGeneric(VirtualFrame frame) {
try {
LLVMException exception = (LLVMException) frame.getObject(exceptionSlot);
LLVMAddress exceptionInfo = exception.getPointer();
LLVMAddress unwindHeader = getGetUnwindHeader().getUnwind(exceptionInfo);
LLVMAddress thrownTypeID = getGetExceptionType().get(unwindHeader);
int clauseId = getEntryIdentifier(frame, exceptionInfo, thrownTypeID);
if (clauseId == 0 && !cleanup) {
throw exception;
} else {
LLVMAddress executeLLVMAddress = allocateLandingPadValue.executeLLVMAddress(frame);
LLVMAddress pair0 = executeLLVMAddress;
LLVMMemory.putAddress(pair0, unwindHeader);
LLVMMemory.putI32(executeLLVMAddress.getVal() + LLVMExpressionNode.ADDRESS_SIZE_IN_BYTES, clauseId);
return executeLLVMAddress;
}
} catch (FrameSlotTypeException | UnexpectedResultException e) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException(e);
}
}
@ExplodeLoop
private int getEntryIdentifier(VirtualFrame frame, LLVMAddress exceptionInfo, LLVMAddress thrownTypeID) {
for (int i = 0; i < entries.length; i++) {
int clauseId = entries[i].getIdentifier(frame, exceptionInfo, thrownTypeID);
if (clauseId != 0) {
return clauseId;
}
}
return 0;
}
public abstract static class LandingpadEntryNode extends LLVMExpressionNode {
public abstract int getIdentifier(VirtualFrame frame, LLVMAddress exceptionInfo, LLVMAddress thrownTypeID);
@Override
public final Object executeGeneric(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException();
}
}
public static final class LandingpadCatchEntryNode extends LandingpadEntryNode {
@Child private LLVMExpressionNode catchType;
@Child private LLVMForceLLVMAddressNode forceToLLVMcatchType;
@Child private LLVMNativeFunctions.SulongCanCatchNode canCatch;
public LandingpadCatchEntryNode(LLVMExpressionNode catchType) {
this.catchType = catchType;
this.forceToLLVMcatchType = getForceLLVMAddressNode();
}
public LLVMNativeFunctions.SulongCanCatchNode getCanCatch() {
if (canCatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.canCatch = insert(getContext().getNativeFunctions().createSulongCanCatch());
}
return canCatch;
}
@Override
public int getIdentifier(VirtualFrame frame, LLVMAddress exceptionInfo, LLVMAddress thrownTypeID) {
LLVMAddress catchAddress = forceToLLVMcatchType.executeWithTarget(catchType.executeGeneric(frame));
if (catchAddress.getVal() == 0) {
/*
* If ExcType is null, any exception matches, so the landing pad should always be
* entered. catch (...)
*/
return 1;
}
if (getCanCatch().canCatch(exceptionInfo, thrownTypeID, catchAddress) != 0) {
return (int) catchAddress.getVal();
}
return 0;
}
}
public static final class LandingpadFilterEntryNode extends LandingpadEntryNode {
@Children private final LLVMExpressionNode[] filterTypes;
@Children private final LLVMForceLLVMAddressNode[] forceToLLVMfilterTypes;
@Child private LLVMNativeFunctions.SulongCanCatchNode canCatch;
public LandingpadFilterEntryNode(LLVMExpressionNode[] filterTypes) {
this.filterTypes = filterTypes;
this.forceToLLVMfilterTypes = getForceLLVMAddressNodes(filterTypes.length);
}
public LLVMNativeFunctions.SulongCanCatchNode getCanCatch() {
if (canCatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.canCatch = insert(getContext().getNativeFunctions().createSulongCanCatch());
}
return canCatch;
}
@Override
public int getIdentifier(VirtualFrame frame, LLVMAddress exceptionInfo, LLVMAddress thrownTypeID) {
if (!filterMatches(frame, exceptionInfo, thrownTypeID)) {
// when this clause is matched, the selector value has to be negative
return -1;
}
return 0;
}
@ExplodeLoop
private boolean filterMatches(VirtualFrame frame, LLVMAddress exceptionInfo, LLVMAddress thrownTypeID) {
/*
* Landingpad should be entered if the exception being thrown does not match any of the
* types in the list
*/
for (int i = 0; i < filterTypes.length; i++) {
LLVMAddress filterAddress = forceToLLVMfilterTypes[i].executeWithTarget(filterTypes[i].executeGeneric(frame));
if (filterAddress.getVal() == 0) {
/*
* If ExcType is null, any exception matches, so the landing pad should always
* be entered. catch (...)
*/
return true;
}
if (getCanCatch().canCatch(exceptionInfo, thrownTypeID, filterAddress) != 0) {
return true;
}
}
return false;
}
}
}