Skip to content

Commit

Permalink
ffi
Browse files Browse the repository at this point in the history
  • Loading branch information
acertain committed May 9, 2021
1 parent 5990930 commit b4189c7
Show file tree
Hide file tree
Showing 14 changed files with 535 additions and 158 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
**/*.iml
**/.idea
**/build/*
build/*
**/dist-newstyle/**
**/dist/**
*.ipr
Expand Down
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ application {
"--module-path=${compiler.asPath}",
"--upgrade-module-path=${compiler.asPath}",
"--add-opens=jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime=ALL-UNNAMED",
"--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED",
"--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED",
"-Dtruffle.class.path.append=@TRUFFLESTG_APP_HOME@/lib/trufflestg-${project.version}.jar",
"-Xss32m"
)
Expand All @@ -150,6 +152,8 @@ val graalArgs = listOf(
"--upgrade-module-path=${compiler.asPath}",
// "-XX:-UseJVMCIClassLoader",
// "-Dgraalvm.locatorDisabled=true",
"--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED",
"--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED",
"--add-opens=jdk.internal.vm.compiler/org.graalvm.compiler.truffle.runtime=ALL-UNNAMED",
"-Dtruffle.class.path.append=build/libs/trufflestg-${project.version}.jar",
"-Xss32m"
Expand Down Expand Up @@ -252,6 +256,7 @@ tasks.withType<ProcessResources> {
from(files("etc/symlinks","etc/permissions")) {
rename("(.*)","META-INF/$1")
}
from("src/c/librts.so")
}

tasks.withType<DokkaTask> {
Expand Down
41 changes: 41 additions & 0 deletions src/c/rts.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

// TODO
extern int64_t base_GHCziTopHandler_runIO_closure[] = {};

// TODO: figure out how to deal with foreign exports
// look at Module.foreignStubs
extern int64_t base_ControlziConcurrent_zdfstableZZC0ZZCbaseZZCControlzziConcurrentZZCforkOSzzuentry_closure[] = {};

extern bool keepCAFs = false;

extern void* stable_ptr_table = NULL;
extern unsigned int n_capabilities = 4;
extern int64_t RtsFlags[1048576] = {0}; // is actually a struct, allocate 8MB to be safe

void unblockUserSignals(void) {}
void blockUserSignals(void) {}

// used for profiling, so we don't need it i think
void startTimer(void) {}
void stopTimer(void) {}


// TODO
void debugBelch() {}
void errorBelch() {}
void _assertFail() {}
void rts_mkStablePtr() {}
void rts_unlock() {}
void rts_lock() {}
void rts_checkSchedStatus() {}
void rts_mkInt32() {}
void rts_getInt32() {}
void rts_evalIO() {}
void rts_apply() {}
void foreignExportStablePtr() {}
void getProcessElapsedTime() {}

void __dummy_hfehfewhoihjofijwoiefhua() {}
6 changes: 2 additions & 4 deletions src/trufflestg/data/AlgData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,6 @@ class DynamicClassDataConInfo(
// TODO: use CompilerDirectives.isExact once its released
if (klass!!.isInstance(x)) CompilerDirectives.castExact(x, klass) else null

// TODO: unbox fields, and set field type when we know it can't be a thunk (bang patterns)
// ghc-wpc isn't telling us about bang patterns though :(
// maybe could look at worker type?
private val klass: Class<DataCon>? = if (size == 0) null else run {
// TODO: better mangling
fun mangle(x: String) = x.replace("""[\[\]]""".toRegex(), "_")
Expand All @@ -169,6 +166,8 @@ class DynamicClassDataConInfo(
is Stg.PrimRep.IntRep -> stgIntFieldInfo
is Stg.PrimRep.WordRep -> stgWordFieldInfo
is Stg.PrimRep.LiftedRep -> objectFieldInfo
// TODO: set field type when we know it (bang patterns)
// need to add bang pattern & type info to ghc-wpc
else -> {
// println("todo PrimRep $it")
objectFieldInfo
Expand All @@ -195,7 +194,6 @@ class DynamicClassDataConInfo(
val field = kls.getDeclaredField("_info")
field.set(null, this)
val modifiers = Field::class.java.getDeclaredField("modifiers")
// TODO: make sure the final from here gets picked up by the jit
modifiers.isAccessible = true
modifiers.setInt(field, field.modifiers and Modifier.FINAL)
modifiers.isAccessible = false
Expand Down
2 changes: 0 additions & 2 deletions src/trufflestg/data/Closure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package trufflestg.data

import com.oracle.truffle.api.CallTarget
import trufflestg.array_utils.*
import trufflestg.frame.DataFrame
import trufflestg.jit.CallUtils
import trufflestg.jit.ClosureRootNode
import trufflestg.panic
import com.oracle.truffle.api.CompilerDirectives
Expand Down
191 changes: 161 additions & 30 deletions src/trufflestg/data/stg_data.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
package trufflestg.data

import trufflestg.panic
import trufflestg.stg.Stg
import com.oracle.truffle.api.CompilerDirectives
import com.oracle.truffle.api.interop.InteropLibrary
import com.oracle.truffle.api.interop.TruffleObject
import com.oracle.truffle.api.interop.UnsupportedMessageException
import com.oracle.truffle.api.library.ExportLibrary
import com.oracle.truffle.api.library.ExportMessage
import jdk.internal.misc.Unsafe
import org.intelligence.diagnostics.Severity
import org.intelligence.diagnostics.error
import org.intelligence.pretty.Pretty
import java.lang.ref.WeakReference
import kotlin.reflect.KClass
import trufflestg.array_utils.write
import trufflestg.jit.asCString
import java.lang.reflect.Constructor
import java.nio.ByteBuffer
import java.nio.ByteOrder

// JvmField should make PE faster and might help graal

// everything here is temporary until i implement unboxed fields etc
// TODO: sealed (or just abstract?) class for possible haskell values
// TODO: CompilerDirectives.ValueType

// VoidRep
object VoidInh

object NullAddr


data class FullName(
val unitId: String,
val module: String,
Expand All @@ -34,50 +35,180 @@ data class FullName(
// note that currently ghc only has Int#, not Int32# etc, so we only need StgInt
// FIXME ghc actually does have various Int*# variants, but they aren't used in Int32 etc? what's going on?
@CompilerDirectives.ValueType
data class StgInt(@JvmField val x: Long) {
@ExportLibrary(InteropLibrary::class)
data class StgInt(@JvmField val x: Long) : TruffleObject {
fun toInt(): Int = x.toInt()
operator fun compareTo(y: StgInt): Int = x.compareTo(y.x)

@ExportMessage fun isNumber(): Boolean = true
@ExportMessage fun fitsInLong(): Boolean = true
@ExportMessage fun asLong(): Long = x

@ExportMessage fun fitsInByte(): Boolean = false
@ExportMessage fun fitsInShort(): Boolean = false
@ExportMessage fun fitsInInt(): Boolean = false
@ExportMessage fun fitsInFloat(): Boolean = false
@ExportMessage fun fitsInDouble(): Boolean = false

@ExportMessage fun asByte(): Byte { throw UnsupportedMessageException.create() }
@ExportMessage fun asShort(): Short { throw UnsupportedMessageException.create() }
@ExportMessage fun asInt(): Int { throw UnsupportedMessageException.create() }
@ExportMessage fun asFloat(): Float { throw UnsupportedMessageException.create() }
@ExportMessage fun asDouble(): Double { throw UnsupportedMessageException.create() }

fun unbox(): Long = x
companion object { @JvmStatic fun box(x: Long): StgInt = StgInt(x) }
}

@CompilerDirectives.ValueType
data class StgWord(@JvmField val x: ULong) {
@OptIn(ExperimentalUnsignedTypes::class)
@ExportLibrary(InteropLibrary::class)
data class StgWord(@JvmField val x: ULong) : TruffleObject {
fun asChar(): Int = x.toInt()

@ExportMessage fun isNumber(): Boolean = true
@ExportMessage fun fitsInLong(): Boolean = true
// TODO: is this right??
@ExportMessage fun asLong(): Long = x.toLong()

@ExportMessage fun fitsInByte(): Boolean = false
@ExportMessage fun fitsInShort(): Boolean = false
@ExportMessage fun fitsInInt(): Boolean = false
@ExportMessage fun fitsInFloat(): Boolean = false
@ExportMessage fun fitsInDouble(): Boolean = false

@ExportMessage fun asByte(): Byte { throw UnsupportedMessageException.create() }
@ExportMessage fun asShort(): Short { throw UnsupportedMessageException.create() }
@ExportMessage fun asInt(): Int { throw UnsupportedMessageException.create() }
@ExportMessage fun asFloat(): Float { throw UnsupportedMessageException.create() }
@ExportMessage fun asDouble(): Double { throw UnsupportedMessageException.create() }

fun unbox(): Long = x.toLong()
companion object { @JvmStatic fun box(x: Long): StgWord = StgWord(x.toULong()) }
}

@CompilerDirectives.ValueType
data class StgDouble(@JvmField val x: Double) {}
data class StgDouble(@JvmField val x: Double)

// for now an Addr# is an offset into an array
// TODO: make arr private & add more methods to avoid errors
class StgAddr(
@JvmField val arr: ByteArray,
@JvmField val offset: Int
) {
operator fun get(ix: Int): Byte = arr[offset + ix]
operator fun set(ix: StgInt, y: Byte) { arr[offset + ix.x.toInt()] = y }
val unsafe: Unsafe = Unsafe.getUnsafe()

fun asArray(): ByteArray = arr.copyOfRange(offset, arr.size)
fun asBuffer(): ByteBuffer = ByteBuffer.wrap(arr, offset, arr.size - offset)
val directBufferCtor: Constructor<*> = run {
val x = Class.forName("java.nio.DirectByteBuffer").getDeclaredConstructor(java.lang.Long.TYPE, java.lang.Integer.TYPE)
x.isAccessible = true
x
}
fun newDirectByteBuffer(addr: Long, cap: Int): ByteBuffer {
return (directBufferCtor.newInstance(addr, cap) as ByteBuffer).order(ByteOrder.LITTLE_ENDIAN)
}

sealed class StgAddr {
abstract fun getRange(x: IntRange): ByteArray
abstract fun write(offset: Int, data: ByteArray)

// x is always in bytes
abstract fun readByte(x: Int): Byte
abstract fun readInt(x: Int): Int
abstract fun readLong(x: Int): Long
abstract fun writeByte(x: Int, y: Byte)
abstract fun writeInt(x: Int, y: Int)
abstract fun writeLong(x: Int, y: Long)

abstract fun addOffset(x: Int): StgAddr

abstract fun asCString(): String

companion object {
fun fromArray(x: ByteArray) = StgArrayOffsetAddr(x, 0)
val nullAddr: StgAddr = StgFFIAddr(0L)
}

// for now an Addr# is an offset into an array
// TODO: make arr private & add more methods to avoid errors
class StgArrayOffsetAddr(
@JvmField val arr: ByteArray,
@JvmField val offset: Int
): StgAddr() {
override fun equals(other: Any?): Boolean = other is StgArrayOffsetAddr && arr === other.arr && offset == other.offset

override fun getRange(x: IntRange): ByteArray = arr.copyOfRange(offset + x.first, offset + x.last + 1)
override fun write(offset: Int, data: ByteArray) = arr.write(offset, data)
override fun readByte(x: Int): Byte = arr[offset + x]
override fun readInt(x: Int): Int = ByteBuffer.wrap(arr).getInt(offset + x)
override fun readLong(x: Int): Long = ByteBuffer.wrap(arr).getLong(offset + x)
override fun writeByte(x: Int, y: Byte) { arr[offset + x] = y }
override fun writeInt(x: Int, y: Int) = arr.write(offset + x, y)
override fun writeLong(x: Int, y: Long) = arr.write(offset + x, y)

override fun addOffset(x: Int): StgAddr = StgArrayOffsetAddr(arr, offset + x)

override fun asCString(): String = asArray().asCString()

fun asArray() = arr.copyOfRange(offset, arr.size)

operator fun get(ix: Int): Byte = arr[offset + ix]
operator fun set(ix: StgInt, y: Byte) { arr[offset + ix.x.toInt()] = y }

}

@ExportLibrary(InteropLibrary::class)
class StgFFIAddr(@JvmField val addr: Long): StgAddr(), TruffleObject {
override fun equals(other: Any?): Boolean = other is StgFFIAddr && addr == other.addr
override fun toString(): String = "StgFFIAddr($addr)"

@ExportMessage fun isPointer(): Boolean = true
@ExportMessage fun asPointer(): Long = addr

// TODO: unsafe's doc implies it's undefined to use it on addresses not from the jvm, figure out if that is actually true and if so replace it with something else

override fun getRange(x: IntRange): ByteArray {
val size = x.last - x.first + 1
val arr = ByteArray(size)
unsafe.copyMemory(null, addr, arr, Unsafe.ARRAY_BYTE_BASE_OFFSET.toLong(), size.toLong())
return arr
}
override fun write(offset: Int, data: ByteArray) { unsafe.copyMemory(data, (Unsafe.ARRAY_BYTE_BASE_OFFSET + offset).toLong(), null, addr, data.size.toLong()) }

override fun readByte(x: Int): Byte = unsafe.getByte(addr + x)
override fun readInt(x: Int): Int = unsafe.getInt(addr + x)
override fun readLong(x: Int): Long = unsafe.getLong(addr + x)

override fun writeByte(x: Int, y: Byte) = unsafe.putByte(addr + x, y)
override fun writeInt(x: Int, y: Int) = unsafe.putInt(addr + x, y)
override fun writeLong(x: Int, y: Long) = unsafe.putLong(addr + x, y)

override fun addOffset(x: Int): StgAddr = StgFFIAddr(addr + x)

override fun asCString(): String {
TODO("Not yet implemented")
}
}
}


// afaict this guy can be mutable (& freeze & unfreeze operate on it)?
// :/
class StgArray(
@JvmField val arr: Array<Any>
) {
operator fun get(ix: StgInt): Any = arr[ix.toInt()]
operator fun set(y: StgInt, value: Any) { arr[y.toInt()] = value }
}

class StgMutableByteArray(
@JvmField val arr: ByteArray
) {
@JvmField var frozen: Boolean = false
sealed class StgByteArray {
abstract fun asAddr(): StgAddr

class StgJvmByteArray(@JvmField val arr: ByteArray) : StgByteArray() {
override fun asAddr(): StgAddr = StgAddr.StgArrayOffsetAddr(arr, 0)
}
class StgPinnedByteArray(@JvmField val len: Long) : StgByteArray() {
@JvmField val addr: Long = unsafe.allocateMemory(len)
class StgFFIByteArrayCleanup(private val addr: Long) : Runnable {
override fun run() { unsafe.freeMemory(addr) }
}
init { jdk.internal.ref.Cleaner.create(this, StgFFIByteArrayCleanup(addr)) }

fun asBuffer(): ByteBuffer = ByteBuffer.wrap(arr)
override fun asAddr(): StgAddr = StgAddr.StgFFIAddr(addr)
}
}

//class StgByteArray(
Expand Down Expand Up @@ -120,7 +251,7 @@ class WeakRef(
// TODO: UnboxedTuple1 etc?
@CompilerDirectives.ValueType
class UnboxedTuple(
@JvmField val x: Array<Any>
@JvmField @CompilerDirectives.CompilationFinal(dimensions = 1) val x: Array<Any>
) {
override fun toString(): String = "(# " + x.joinToString(", ") { it.toString() } + " #)"
}
Expand All @@ -130,8 +261,8 @@ class ThreadId(
@JvmField val id: Long
)


// TODO: store a weakref here
class StablePtr(
@JvmField var x: Any?
)

5 changes: 1 addition & 4 deletions src/trufflestg/frame/DataFrame.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package trufflestg.frame

import com.oracle.truffle.api.frame.FrameSlotTypeException

typealias Slot = Int
// these are all the distinctions the JVM cares about
// TODO: i'm currently only using getValue, and probably won't use any of the others

// TODO: make this an abstract class: casting to superclasses should be faster than casting to interfaces
interface DataFrame {
fun getValue(slot: Slot): Any?
Expand Down
1 change: 0 additions & 1 deletion src/trufflestg/frame/frame_assembly.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ fun ClassNode.frameBody(types: Array<FieldInfo>, superCls: Type = type(Object::c
iconst_0
istore_2

// FIXME: is this wrong? i think it needs to unbox? (only works for object as is?)
for (i in types.indices) {
aload_0
aload_1
Expand Down
Loading

0 comments on commit b4189c7

Please sign in to comment.