package com.oracle.truffle.llvm.runtime.global;
import com.oracle.truffle.api.Assumption;
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.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.CachedLanguage;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.llvm.runtime.LLVMLanguage;
import com.oracle.truffle.llvm.runtime.interop.LLVMInternalTruffleObject;
import com.oracle.truffle.llvm.runtime.library.internal.LLVMManagedReadLibrary;
import com.oracle.truffle.llvm.runtime.library.internal.LLVMManagedWriteLibrary;
import com.oracle.truffle.llvm.runtime.library.internal.LLVMNativeLibrary;
import com.oracle.truffle.llvm.runtime.memory.LLVMMemory;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMToNativeNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMToPointerNode;
import com.oracle.truffle.llvm.runtime.pointer.LLVMNativePointer;
import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer;
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(LLVMManagedReadLibrary.class)
@ExportLibrary(LLVMManagedWriteLibrary.class)
public final class LLVMGlobalContainer extends LLVMInternalTruffleObject {
private static final int MAX_CACHED_WRITES = 3;
private static final class State {
final Object value;
final Assumption assumption;
final int writeCount;
State(Object value, int writeCount) {
assert writeCount <= MAX_CACHED_WRITES;
this.value = value;
this.writeCount = writeCount;
this.assumption = Truffle.getRuntime().createAssumption();
}
}
private long address;
@CompilationFinal private State contents;
private Object fallbackContents;
public LLVMGlobalContainer() {
contents = new State(0L, 0);
}
public Object get() {
while (true) {
State c = contents;
if (c.assumption.isValid()) {
return c.value;
}
if (c.writeCount == MAX_CACHED_WRITES) {
return fallbackContents;
}
CompilerDirectives.transferToInterpreterAndInvalidate();
}
}
public void set(Object value, BranchProfile needsInitialize, BranchProfile needsInvalidation) {
State c = contents;
if (c.writeCount < MAX_CACHED_WRITES) {
needsInitialize.enter();
contents = new State(value, c.writeCount + 1);
c.assumption.invalidate();
} else {
if (c.assumption.isValid()) {
needsInvalidation.enter();
c.assumption.invalidate();
}
fallbackContents = value;
}
}
@ExportMessage
public boolean isPointer() {
return address != 0;
}
@ExportMessage
public long asPointer() throws UnsupportedMessageException {
if (isPointer()) {
return address;
} else {
throw UnsupportedMessageException.create();
}
}
public long getAddress() {
return address;
}
@SuppressWarnings("static-method")
public int getSize() {
return 1;
}
@TruffleBoundary
@ExportMessage
public void toNative(@Cached LLVMToNativeNode toNative) {
if (address == 0) {
LLVMMemory memory = LLVMLanguage.getLanguage().getLLVMMemory();
LLVMNativePointer pointer = memory.allocateMemory(toNative, 8);
address = pointer.asNative();
long value;
Object global = get();
if (global instanceof Number) {
value = ((Number) global).longValue();
} else {
value = toNative.executeWithTarget(global).asNative();
}
memory.putI64(toNative, pointer, value);
}
}
@ExportMessage(name = "isReadable")
@ExportMessage(name = "isWritable")
@SuppressWarnings("static-method")
boolean isAccessible() {
return true;
}
@ExportMessage
static class ReadI8 {
@Specialization(guards = "self.isPointer()")
static byte readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getI8(location, self.getAddress() + offset);
}
@Specialization(guards = "!self.isPointer()")
static byte readManaged(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readI8(self, offset);
}
}
@ExportMessage
static class ReadI16 {
@Specialization(guards = "self.isPointer()")
static short readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getI16(location, self.getAddress() + offset);
}
@Specialization(guards = "!self.isPointer()")
static short readManaged(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readI16(self, offset);
}
}
@ExportMessage
static class ReadI32 {
@Specialization(guards = "self.isPointer()")
static int readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getI32(location, self.getAddress() + offset);
}
@Specialization(guards = "!self.isPointer()")
static int readManaged(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readI32(self, offset);
}
}
@ExportMessage
static class ReadFloat {
@Specialization(guards = "self.isPointer()")
static float readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getFloat(location, self.getAddress() + offset);
}
@Specialization(guards = "!self.isPointer()")
static float readManaged(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readFloat(self, offset);
}
}
@ExportMessage
static class ReadDouble {
@Specialization(guards = "self.isPointer()")
static double readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getDouble(location, self.getAddress() + offset);
}
@Specialization(guards = "!self.isPointer()")
static double readManaged(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readDouble(self, offset);
}
}
@ExportMessage
static class ReadGenericI64 {
@Specialization(guards = "self.isPointer()")
static long readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getI64(location, self.getAddress() + offset);
}
@Specialization(guards = {"!self.isPointer()", "offset == 0"})
static Object readManaged(LLVMGlobalContainer self, long offset) {
assert offset == 0;
return self.get();
}
@Specialization(guards = {"!self.isPointer()", "offset != 0"})
static Object readFallback(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readGenericI64(self, offset);
}
}
@ExportMessage
static class ReadPointer {
@Specialization(guards = "self.isPointer()")
static LLVMPointer readNative(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") LLVMManagedReadLibrary location,
@CachedLanguage LLVMLanguage language) {
return language.getLLVMMemory().getPointer(location, self.getAddress() + offset);
}
@Specialization(guards = {"!self.isPointer()", "offset == 0"})
static LLVMPointer readManaged(LLVMGlobalContainer self, long offset,
@Cached LLVMToPointerNode toPointer) {
assert offset == 0;
return toPointer.executeWithTarget(self.get());
}
@Specialization(guards = {"!self.isPointer()", "offset != 0"})
static LLVMPointer readFallback(LLVMGlobalContainer self, long offset,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedReadLibrary read) {
interop.toNative(self);
return read.readPointer(self, offset);
}
}
@ExportMessage
static class WriteI8 {
@Specialization(guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, byte value,
@CachedLibrary("self") LLVMManagedWriteLibrary location,
@CachedLanguage LLVMLanguage language) {
language.getLLVMMemory().putI8(location, self.getAddress() + offset, value);
}
@Specialization(guards = "!self.isPointer()")
static void writeManaged(LLVMGlobalContainer self, long offset, byte value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeI8(self, offset, value);
}
}
@ExportMessage
static class WriteI16 {
@Specialization(guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, short value,
@CachedLibrary("self") LLVMManagedWriteLibrary location,
@CachedLanguage LLVMLanguage language) {
language.getLLVMMemory().putI16(location, self.getAddress() + offset, value);
}
@Specialization(guards = "!self.isPointer()")
static void writeManaged(LLVMGlobalContainer self, long offset, short value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeI16(self, offset, value);
}
}
@ExportMessage
static class WriteI32 {
@Specialization(guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, int value,
@CachedLibrary("self") LLVMManagedWriteLibrary location,
@CachedLanguage LLVMLanguage language) {
language.getLLVMMemory().putI32(location, self.getAddress() + offset, value);
}
@Specialization(guards = "!self.isPointer()")
static void writeManaged(LLVMGlobalContainer self, long offset, int value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeI32(self, offset, value);
}
}
@ExportMessage
static class WriteFloat {
@Specialization(guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, float value,
@CachedLibrary("self") LLVMManagedWriteLibrary location,
@CachedLanguage LLVMLanguage language) {
language.getLLVMMemory().putFloat(location, self.getAddress() + offset, value);
}
@Specialization(guards = "!self.isPointer()")
static void writeManaged(LLVMGlobalContainer self, long offset, float value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeFloat(self, offset, value);
}
}
@ExportMessage
static class WriteDouble {
@Specialization(guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, double value,
@CachedLibrary("self") LLVMManagedWriteLibrary location,
@CachedLanguage LLVMLanguage language) {
language.getLLVMMemory().putDouble(location, self.getAddress() + offset, value);
}
@Specialization(guards = "!self.isPointer()")
static void writeManaged(LLVMGlobalContainer self, long offset, double value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeDouble(self, offset, value);
}
}
@ExportMessage
static class WriteI64 {
@Specialization(guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, long value,
@CachedLibrary("self") LLVMManagedWriteLibrary location,
@CachedLanguage LLVMLanguage language) {
language.getLLVMMemory().putI64(location, self.getAddress() + offset, value);
}
@Specialization(guards = {"!self.isPointer()", "offset == 0"})
static void writeManaged(LLVMGlobalContainer self, long offset, long value,
@Shared("p1") @Cached BranchProfile needsInitialize,
@Shared("p2") @Cached BranchProfile needsInvalidation) {
assert offset == 0;
self.set(value, needsInitialize, needsInvalidation);
}
@Specialization(guards = {"!self.isPointer()", "offset != 0"})
static void writeFallback(LLVMGlobalContainer self, long offset, long value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeI64(self, offset, value);
}
}
@ExportMessage
static class WriteGenericI64 {
@Specialization(limit = "3", guards = "self.isPointer()")
static void writeNative(LLVMGlobalContainer self, long offset, Object value,
@CachedLibrary("value") LLVMNativeLibrary toNative,
@CachedLanguage LLVMLanguage language) {
long ptr = toNative.toNativePointer(value).asNative();
language.getLLVMMemory().putI64(toNative, self.getAddress() + offset, ptr);
}
@Specialization(guards = {"!self.isPointer()", "offset == 0"})
static void writeManaged(LLVMGlobalContainer self, long offset, Object value,
@Shared("p1") @Cached BranchProfile needsInitialize,
@Shared("p2") @Cached BranchProfile needsInvalidation) {
assert offset == 0;
self.set(value, needsInitialize, needsInvalidation);
}
@Specialization(guards = {"!self.isPointer()", "offset != 0"})
static void writeFallback(LLVMGlobalContainer self, long offset, Object value,
@CachedLibrary("self") InteropLibrary interop,
@CachedLibrary("self") LLVMManagedWriteLibrary write) {
interop.toNative(self);
write.writeGenericI64(self, offset, value);
}
}
public void dispose() {
if (address != 0) {
LLVMMemory memory = LLVMLanguage.getLanguage().getLLVMMemory();
memory.free(null, address);
address = 0;
}
}
@Override
@TruffleBoundary
public String toString() {
return String.format("LLVMGlobalContainer (address = 0x%x, contents = %s)", address, contents);
}
}