/*
* 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.runtime.interop;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.llvm.runtime.LLVMAddress;
import com.oracle.truffle.llvm.runtime.LLVMPerformance;
import com.oracle.truffle.llvm.runtime.LLVMSharedGlobalVariable;
import com.oracle.truffle.llvm.runtime.LLVMTruffleAddress;
import com.oracle.truffle.llvm.runtime.global.LLVMGlobalVariable;
import com.oracle.truffle.llvm.runtime.memory.LLVMMemory;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.types.PointerType;
import com.oracle.truffle.llvm.runtime.types.PrimitiveType;
import com.oracle.truffle.llvm.runtime.types.Type;
@SuppressWarnings("unused")
abstract class LLVMAddressMessageResolutionNode extends Node {
private static final int I1_SIZE = 1;
private static final int I8_SIZE = 1;
private static final int I16_SIZE = 2;
private static final int I32_SIZE = 4;
private static final int I64_SIZE = 8;
private static final int FLOAT_SIZE = 4;
private static final int DOUBLE_SIZE = 8;
public Type getType(LLVMTruffleAddress receiver) {
return receiver.getType();
}
public PrimitiveType getPointeeType(LLVMTruffleAddress receiver) {
Type t = receiver.getType();
if (t instanceof PointerType && ((PointerType) t).getPointeeType() instanceof PrimitiveType) {
return (PrimitiveType) ((PointerType) t).getPointeeType();
} else {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException(t.toString());
}
}
public PrimitiveType getPointeeType(LLVMGlobalVariable receiver) {
Type t = receiver.getType();
if (t instanceof PrimitiveType) {
return (PrimitiveType) t;
} else {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException(t.toString());
}
}
public LLVMDataEscapeNode getPrepareValueForEscapeNode(Type t) {
return LLVMDataEscapeNodeGen.create(t);
}
public boolean typeGuard(LLVMTruffleAddress receiver, Type type) {
return receiver.getType() == (type);
}
public ToLLVMNode getToLLVMNode(PrimitiveType primitiveType) {
return ToLLVMNode.createNode(ToLLVMNode.convert(primitiveType));
}
public ToLLVMNode getToTruffleObjectLLVMNode() {
return ToLLVMNode.createNode((TruffleObject.class));
}
abstract static class LLVMAddressReadMessageResolutionNode extends LLVMAddressMessageResolutionNode {
public abstract Object executeWithTarget(VirtualFrame frame, Object receiver, int index);
@Specialization(guards = {"index == cachedIndex", "typeGuard(receiver, cachedType)"})
public Object doCachedTypeCachedOffset(LLVMTruffleAddress receiver, int index,
@Cached("getType(receiver)") Type cachedType,
@Cached("index") int cachedIndex,
@Cached("getPointeeType(receiver)") PrimitiveType elementType,
@Cached("getPrepareValueForEscapeNode(elementType)") LLVMDataEscapeNode prepareValueForEscape) {
return prepareValueForEscape.executeWithTarget(doRead(receiver, elementType, cachedIndex), receiver.getContext());
}
@Specialization(guards = {"typeGuard(receiver, cachedType)"}, replaces = "doCachedTypeCachedOffset")
public Object doCachedType(LLVMTruffleAddress receiver, int index,
@Cached("getType(receiver)") Type cachedType,
@Cached("getPointeeType(receiver)") PrimitiveType elementType,
@Cached("getPrepareValueForEscapeNode(elementType)") LLVMDataEscapeNode prepareValueForEscape) {
return prepareValueForEscape.executeWithTarget(doRead(receiver, elementType, index), receiver.getContext());
}
@Specialization(replaces = {"doCachedTypeCachedOffset", "doCachedType"})
public Object doRegular(LLVMTruffleAddress receiver, int index) {
LLVMPerformance.warn(this);
if (receiver.getType() instanceof PointerType && ((PointerType) receiver.getType()).getPointeeType() instanceof PrimitiveType) {
return LLVMDataEscapeNode.slowConvert(doRead(receiver, (PrimitiveType) ((PointerType) receiver.getType()).getPointeeType(), index), getPointeeType(receiver), receiver.getContext());
} else {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException(receiver.getType().toString());
}
}
private static Object doRead(LLVMTruffleAddress receiver, PrimitiveType elemntType, int cachedIndex) {
LLVMAddress address = receiver.getAddress();
return doPrimitiveRead(cachedIndex, address, elemntType);
}
private static Object doPrimitiveRead(int cachedIndex, LLVMAddress address, PrimitiveType primitiveType) {
long ptr = address.getVal();
switch (primitiveType.getPrimitiveKind()) {
case I1:
return LLVMMemory.getI1(ptr + cachedIndex * I1_SIZE);
case I8:
return LLVMMemory.getI8(ptr + cachedIndex * I8_SIZE);
case I16:
return LLVMMemory.getI16(ptr + cachedIndex * I16_SIZE);
case I32:
return LLVMMemory.getI32(ptr + cachedIndex * I32_SIZE);
case I64:
return LLVMMemory.getI64(ptr + cachedIndex * I64_SIZE);
case FLOAT:
return LLVMMemory.getFloat(ptr + cachedIndex * FLOAT_SIZE);
case DOUBLE:
return LLVMMemory.getDouble(ptr + cachedIndex * DOUBLE_SIZE);
default:
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException();
}
}
@Specialization(guards = "receiver.getDescriptor() == cachedReceiver")
public Object doGlobalCached(LLVMSharedGlobalVariable receiver, int index, @Cached("receiver.getDescriptor()") LLVMGlobalVariable cachedReceiver,
@Cached("cachedReceiver.getType()") Type elementType,
@Cached("getPrepareValueForEscapeNode(elementType)") LLVMDataEscapeNode prepareValueForEscape) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
return prepareValueForEscape.executeWithTarget(cachedReceiver.get(), receiver.getContext());
}
@Specialization(replaces = "doGlobalCached")
public Object doGlobal(LLVMSharedGlobalVariable receiver, int index) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
return LLVMDataEscapeNode.slowConvert(receiver.getDescriptor().get(), receiver.getDescriptor().getType(), receiver.getContext());
}
}
abstract static class LLVMAddressWriteMessageResolutionNode extends LLVMAddressMessageResolutionNode {
public abstract Object executeWithTarget(VirtualFrame frame, Object receiver, int index, Object value);
@Specialization(guards = {"index == cachedIndex", "typeGuard(receiver, cachedType)"})
public Object doCachedTypeCachedOffset(LLVMTruffleAddress receiver, int index, Object value,
@Cached("getType(receiver)") Type cachedType,
@Cached("index") int cachedIndex,
@Cached("getPointeeType(receiver)") PrimitiveType elementType,
@Cached("getToLLVMNode(elementType)") ToLLVMNode toLLVM) {
doFastWrite(receiver, elementType, cachedIndex, value, toLLVM);
return value;
}
@Specialization(guards = {"typeGuard(receiver, cachedType)"}, replaces = "doCachedTypeCachedOffset")
public Object doCachedType(LLVMTruffleAddress receiver, int index, Object value,
@Cached("getType(receiver)") Type cachedType,
@Cached("getPointeeType(receiver)") PrimitiveType elementType,
@Cached("getToLLVMNode(elementType)") ToLLVMNode toLLVM) {
doFastWrite(receiver, elementType, index, value, toLLVM);
return value;
}
@Child private ToLLVMNode slowConvert;
@Specialization(replaces = {"doCachedTypeCachedOffset", "doCachedType"})
public Object doRegular(LLVMTruffleAddress receiver, int index, Object value) {
LLVMPerformance.warn(this);
if (slowConvert == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.slowConvert = insert(ToLLVMNode.createNode(null));
}
if (receiver.getType() instanceof PointerType && ((PointerType) receiver.getType()).getPointeeType() instanceof PrimitiveType) {
doSlowWrite(receiver, (PrimitiveType) ((PointerType) receiver.getType()).getPointeeType(), index, value, slowConvert);
} else {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException(receiver.getType().toString());
}
return value;
}
private static void doFastWrite(LLVMTruffleAddress receiver, PrimitiveType cachedType, int index, Object value, ToLLVMNode toLLVM) {
Object v = toLLVM.executeWithTarget(value);
doWrite(receiver, cachedType, index, v);
}
private static void doSlowWrite(LLVMTruffleAddress receiver, PrimitiveType cachedType, int index, Object value, ToLLVMNode toLLVM) {
Object v = toLLVM.slowConvert(value, ToLLVMNode.convert(cachedType));
doWrite(receiver, cachedType, index, v);
}
private static void doWrite(LLVMTruffleAddress receiver, PrimitiveType primitiveType, int index, Object v) {
LLVMAddress address = receiver.getAddress();
doPrimitiveWrite(index, v, address, primitiveType);
}
private static void doPrimitiveWrite(int index, Object v, LLVMAddress address, PrimitiveType primitiveType) {
long ptr = address.getVal();
switch (primitiveType.getPrimitiveKind()) {
case I1:
LLVMMemory.putI1(ptr + index * I1_SIZE, (boolean) v);
break;
case I8:
LLVMMemory.putI8(ptr + index * I8_SIZE, (byte) v);
break;
case I16:
LLVMMemory.putI16(ptr + index * I16_SIZE, (short) v);
break;
case I32:
LLVMMemory.putI32(ptr + index * I32_SIZE, (int) v);
break;
case I64:
LLVMMemory.putI64(ptr + index * I64_SIZE, (long) v);
break;
case FLOAT:
LLVMMemory.putFloat(ptr + index * FLOAT_SIZE, (float) v);
break;
case DOUBLE:
LLVMMemory.putDouble(ptr + index * DOUBLE_SIZE, (double) v);
break;
default:
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException();
}
}
public boolean isPointerTypeGlobal(LLVMSharedGlobalVariable global) {
return global.getDescriptor().getType() instanceof PointerType;
}
public boolean isPrimitiveTypeGlobal(LLVMSharedGlobalVariable global) {
return global.getDescriptor().getType() instanceof PrimitiveType;
}
public boolean isPrimitiveTypeGlobal(LLVMGlobalVariable global) {
return global.getType() instanceof PrimitiveType;
}
public boolean isPointerTypeGlobal(LLVMGlobalVariable global) {
return global.getType() instanceof PointerType;
}
public boolean notLLVM(TruffleObject object) {
return LLVMExpressionNode.notLLVM(object);
}
public boolean notTruffleObject(Object object) {
return !(object instanceof TruffleObject);
}
@Specialization(guards = {"receiver.getDescriptor() == cachedReceiver", "isPointerTypeGlobal(cachedReceiver)", "notTruffleObject(value)"})
public Object doPrimitiveToPointerCached(LLVMSharedGlobalVariable receiver, int index, Object value,
@Cached("receiver.getDescriptor()") LLVMGlobalVariable cachedReceiver, @Cached("getToTruffleObjectLLVMNode()") ToLLVMNode toLLVM) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
TruffleObject convertedValue = (TruffleObject) toLLVM.executeWithTarget(value);
cachedReceiver.putTruffleObject(convertedValue);
return value;
}
@Specialization(guards = {"isPointerTypeGlobal(receiver)", "notTruffleObject(value)"}, replaces = "doPrimitiveToPointerCached")
public Object doPrimitiveToPointer(LLVMSharedGlobalVariable receiver, int index, Object value, @Cached("getToTruffleObjectLLVMNode()") ToLLVMNode toLLVM) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
TruffleObject convertedValue = (TruffleObject) toLLVM.executeWithTarget(value);
receiver.getDescriptor().putTruffleObject(convertedValue);
return convertedValue;
}
@Specialization(guards = {"receiver.getDescriptor() == cachedReceiver", "isPointerTypeGlobal(cachedReceiver)", "notLLVM(value)"})
public Object doGlobalTruffleObjectCached(LLVMSharedGlobalVariable receiver, int index, TruffleObject value,
@Cached("receiver.getDescriptor()") LLVMGlobalVariable cachedReceiver) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
cachedReceiver.putTruffleObject(value);
return value;
}
@Specialization(guards = {"isPointerTypeGlobal(receiver)", "notLLVM(value)"}, replaces = "doGlobalTruffleObjectCached")
public Object doGlobalTruffleObject(LLVMSharedGlobalVariable receiver, int index, TruffleObject value) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
receiver.getDescriptor().putTruffleObject(value);
return value;
}
@Specialization(guards = {"receiver.getDescriptor() == cachedReceiver", "isPrimitiveTypeGlobal(cachedReceiver)"})
public Object doGlobalCached(LLVMSharedGlobalVariable receiver, int index, Object value,
@Cached("receiver.getDescriptor()") LLVMGlobalVariable cachedReceiver,
@Cached("getPointeeType(cachedReceiver)") PrimitiveType cachedType,
@Cached("getToLLVMNode(cachedType)") ToLLVMNode toLLVM) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
doFastWrite(cachedReceiver, cachedType, value, toLLVM);
return value;
}
@Specialization(guards = "isPrimitiveTypeGlobal(receiver)", replaces = "doGlobalCached")
public Object doGlobal(LLVMSharedGlobalVariable receiver, int index, Object value) {
if (index != 0) {
CompilerDirectives.transferToInterpreter();
throw new IllegalStateException("Index must be 0 for globals!");
}
LLVMPerformance.warn(this);
if (slowConvert == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.slowConvert = insert(ToLLVMNode.createNode(null));
}
if (receiver.getDescriptor().getType() instanceof PrimitiveType) {
doSlowWrite(receiver.getDescriptor(), (PrimitiveType) receiver.getDescriptor().getType(), value, slowConvert);
} else {
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException(receiver.getDescriptor().getType().toString());
}
return value;
}
private static void doFastWrite(LLVMGlobalVariable receiver, PrimitiveType cachedType, Object value, ToLLVMNode toLLVM) {
Object v = toLLVM.executeWithTarget(value);
doWrite(receiver, cachedType, v);
}
private static void doSlowWrite(LLVMGlobalVariable receiver, PrimitiveType type, Object value, ToLLVMNode toLLVM) {
Object v = toLLVM.slowConvert(value, ToLLVMNode.convert(type));
doWrite(receiver, type, v);
}
private static void doWrite(LLVMGlobalVariable receiver, PrimitiveType cachedType, Object v) {
doPrimitiveWrite(receiver, v, cachedType);
}
private static void doPrimitiveWrite(LLVMGlobalVariable address, Object v, PrimitiveType primitiveType) {
switch (primitiveType.getPrimitiveKind()) {
case I1:
address.putI1((boolean) v);
break;
case I8:
address.putI8((byte) v);
break;
case I16:
address.putI16((short) v);
break;
case I32:
address.putI32((int) v);
break;
case I64:
address.putI64((long) v);
break;
case FLOAT:
address.putFloat((float) v);
break;
case DOUBLE:
address.putDouble((double) v);
break;
default:
CompilerDirectives.transferToInterpreter();
throw new UnsupportedOperationException();
}
}
}
}