diff --git a/build.gradle.kts b/build.gradle.kts index 8201457..957492e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.5.2") implementation("org.jetbrains.kotlin:kotlin-script-runtime:1.4.32") implementation("org.jetbrains.kotlin:kotlin-reflect:1.4.32") + implementation("com.github.jnr:jnr-posix:3.1.5") } java { diff --git a/src/cbor.kt b/src/cbor.kt index 3219094..d06e9d1 100644 --- a/src/cbor.kt +++ b/src/cbor.kt @@ -115,6 +115,7 @@ fun KClass<*>.isInline(): Boolean { java.declaredMethods.any { it.name == "box-impl" } } +// TODO: use https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/ClassValue.html here? val argsMap: Cache> = Cache { it.arguments.map { x -> x.type }.toTypedArray() } val paramsTypes: Cache, Array> = Cache { it.parameters.map { x -> x.type }.toTypedArray() } val isEnumMap: Cache, Boolean> = Cache { Enum::class.java.isAssignableFrom(it) } diff --git a/src/trufflestg/data/AlgData.kt b/src/trufflestg/data/AlgData.kt index d1dbac5..2913c0c 100644 --- a/src/trufflestg/data/AlgData.kt +++ b/src/trufflestg/data/AlgData.kt @@ -227,7 +227,7 @@ class DynamicClassDataConInfo( when (it) { klass.name -> klass else -> TODO(it) - }} as Class + }} builderKlass.constructors[0].newInstance() as DataFrameBuilder } } diff --git a/src/trufflestg/data/Closure.kt b/src/trufflestg/data/Closure.kt index 1b78931..1ca1e98 100644 --- a/src/trufflestg/data/Closure.kt +++ b/src/trufflestg/data/Closure.kt @@ -22,6 +22,7 @@ import trufflestg.jit.IndirectCallerNode // TODO: selector thunks // TODO: could avoid array allocation on thunk invocation by storing array in Closure/mb having a ThunkClosure class? +// TODO: once we have thunk forwarding could inline Closure into Thunk? class Thunk( @JvmField var clos: Closure?, @JvmField var value_: Any? diff --git a/src/trufflestg/data/stg_data.kt b/src/trufflestg/data/stg_data.kt index 864a5e2..f50c6d3 100644 --- a/src/trufflestg/data/stg_data.kt +++ b/src/trufflestg/data/stg_data.kt @@ -8,6 +8,7 @@ import org.intelligence.diagnostics.error import org.intelligence.pretty.Pretty import java.lang.ref.WeakReference import kotlin.reflect.KClass +import java.nio.ByteBuffer // JvmField should make PE faster and might help graal @@ -53,6 +54,7 @@ class StgAddr( operator fun set(ix: StgInt, y: Byte) { arr[offset + ix.x.toInt()] = y } fun asArray(): ByteArray = arr.copyOfRange(offset, arr.size) + fun asBuffer(): ByteBuffer = ByteBuffer.wrap(arr, offset, arr.size - offset) } // afaict this guy can be mutable (& freeze & unfreeze operate on it)? @@ -67,6 +69,8 @@ class StgMutableByteArray( @JvmField val arr: ByteArray ) { @JvmField var frozen: Boolean = false + + fun asBuffer(): ByteBuffer = ByteBuffer.wrap(arr) } //class StgByteArray( @@ -106,6 +110,7 @@ class WeakRef( ) // TODO: don't store these in closures & etc, only use them in functions, maybe ensureVirtualized +// TODO: UnboxedTuple1 etc? @CompilerDirectives.ValueType class UnboxedTuple( @JvmField val x: Array diff --git a/src/trufflestg/jit/StgFCall.kt b/src/trufflestg/jit/StgFCall.kt index a6cb208..f8f559f 100644 --- a/src/trufflestg/jit/StgFCall.kt +++ b/src/trufflestg/jit/StgFCall.kt @@ -1,26 +1,20 @@ package trufflestg.jit import com.oracle.truffle.api.CompilerDirectives -import com.oracle.truffle.api.Truffle -import com.oracle.truffle.api.TruffleLanguage -import trufflestg.data.* -import trufflestg.panic -import trufflestg.stg.Stg +import com.oracle.truffle.api.exception.AbstractTruffleException import com.oracle.truffle.api.frame.VirtualFrame +import com.oracle.truffle.api.interop.ExceptionType +import com.oracle.truffle.api.interop.InteropLibrary +import com.oracle.truffle.api.library.ExportLibrary +import com.oracle.truffle.api.library.ExportMessage +import com.oracle.truffle.api.nodes.Node import trufflestg.Language import trufflestg.array_utils.* -import java.nio.charset.StandardCharsets +import trufflestg.data.* +import trufflestg.panic +import trufflestg.stg.Stg import java.security.MessageDigest -object GlobalStore { - var GHCConcSignalSignalHandlerStore: Any? = null -} - - -val zeroBytes: ByteArray = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0) - -var globalHeap = ByteArray(0) - class StgFCall( val x: Stg.ForeignCall, @field:Children val args: Array @@ -38,15 +32,25 @@ class StgFCall( } } +fun ByteArray.asCString(): String = String(copyOfRange(0, indexOf(0x00))) +val posix = jnr.posix.POSIXFactory.getPOSIX() +val zeroBytes: ByteArray = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0) -var md5: MessageDigest? = null +@ExportLibrary(InteropLibrary::class) +class TruffleStgExitException(val status: Int) : AbstractTruffleException() { + @ExportMessage fun getExceptionType(): ExceptionType = ExceptionType.EXIT + @ExportMessage fun getExceptionExitStatus(): Int = status +} -@CompilerDirectives.TruffleBoundary -fun ByteArray.asCString(): String = String(copyOfRange(0, indexOf(0x00))) +// mutable state +var md5: MessageDigest? = null +object GlobalStore { + var GHCConcSignalSignalHandlerStore: Any? = null +} +var globalHeap = ByteArray(0) -@CompilerDirectives.TruffleBoundary -fun getMD5(): MessageDigest = MessageDigest.getInstance("MD5") +@OptIn(kotlin.ExperimentalUnsignedTypes::class) val primFCalls: Map StgPrimOp> = mapOf( // TODO: do something safer! (use x to store MessageDigest object?) "__hsbase_MD5Init" to wrap2Boundary { x: StgAddr, y: VoidInh -> @@ -104,6 +108,11 @@ val primFCalls: Map StgPrimOp> = mapOf( panic("nyi ghczuwrapperZC20ZCbaseZCSystemziPosixziInternalsZCwrite") } }, + "ghczuwrapperZC22ZCbaseZCSystemziPosixziInternalsZCread" to wrap4Boundary { x: StgInt, y: StgAddr, z: StgWord, _: VoidInh -> + UnboxedTuple(arrayOf(StgInt(posix.read(x.x.toInt(), y.asBuffer(), z.x.toLong())))) + }, + + "shutdownHaskellAndExit" to wrap3Boundary { x: StgInt, _: StgInt, _: VoidInh -> throw TruffleStgExitException(x.x.toInt()) }, "hs_free_stable_ptr" to wrap2 { x: StablePtr, _: VoidInh -> // FIXME: getting deRefStablePtr after freeStablePtr, so i'm disabling this for now @@ -117,7 +126,7 @@ val primFCalls: Map StgPrimOp> = mapOf( // TODO UnboxedTuple(arrayOf(StgInt(-1))) }, - "getProgArgv" to wrap3Boundary { argc: StgAddr, argv: StgAddr, v: VoidInh -> + "getProgArgv" to wrap3Boundary { argc: StgAddr, argv: StgAddr, _: VoidInh -> val args = arrayOf( "trufflestg", *Language.currentContext().env.applicationArguments @@ -136,10 +145,10 @@ val primFCalls: Map StgPrimOp> = mapOf( argv.arr.write(argv.offset, argvIx.toLong().toByteArray()) UnboxedTuple(arrayOf()) }, - "u_towupper" to wrap2 { x: StgInt, w: VoidInh -> + "u_towupper" to wrap2 { x: StgInt, _: VoidInh -> UnboxedTuple(arrayOf(StgInt(Character.toUpperCase(x.x.toInt()).toLong()))) }, - "u_gencat" to wrap2 { x: StgInt, w: VoidInh -> + "u_gencat" to wrap2 { x: StgInt, _: VoidInh -> UnboxedTuple(arrayOf(StgInt(when (Character.getType(x.x.toInt()).toByte()) { // in java's order, mapping to haskell's order Character.UNASSIGNED -> 29 @@ -197,6 +206,19 @@ val primFCalls: Map StgPrimOp> = mapOf( Character.LOWERCASE_LETTER -> 1 else -> 0 }.toLong()))) + }, + "u_iswalnum" to wrap2 { x: StgInt, _: VoidInh -> + UnboxedTuple(arrayOf(StgInt(when (Character.getType(x.x.toInt()).toByte()) { + Character.UPPERCASE_LETTER -> 1 + Character.LOWERCASE_LETTER -> 1 + Character.TITLECASE_LETTER -> 1 + Character.MODIFIER_LETTER -> 1 + Character.OTHER_LETTER -> 1 + Character.OTHER_NUMBER -> 1 + Character.DECIMAL_DIGIT_NUMBER -> 1 + Character.LETTER_NUMBER -> 1 + else -> 0 + }.toLong()))) } ) @@ -213,4 +235,4 @@ inline fun wrap3Boundary(cros } inline fun wrap4Boundary(crossinline f: (X, Y, Z, A) -> W): () -> StgPrimOp = { object : StgPrimOp4() { @CompilerDirectives.TruffleBoundary override fun execute(x: Any, y: Any, z: Any, a: Any): Any = f((x as? X)!!, (y as? Y)!!, (z as? Z)!!, (a as? A)!!) } -} \ No newline at end of file +} diff --git a/src/trufflestg/jit/StgPrim.kt b/src/trufflestg/jit/StgPrim.kt index 46a061a..72e99e0 100644 --- a/src/trufflestg/jit/StgPrim.kt +++ b/src/trufflestg/jit/StgPrim.kt @@ -3,9 +3,6 @@ package trufflestg.jit import com.oracle.truffle.api.CompilerDirectives import com.oracle.truffle.api.ExactMath -import trufflestg.data.* -import trufflestg.panic -import trufflestg.todo import com.oracle.truffle.api.dsl.TypeSystemReference import com.oracle.truffle.api.frame.VirtualFrame import com.oracle.truffle.api.nodes.ExplodeLoop @@ -13,8 +10,10 @@ import com.oracle.truffle.api.nodes.Node import trufflestg.array_utils.map import trufflestg.array_utils.toByteArray import trufflestg.array_utils.write -import com.oracle.truffle.api.dsl.NodeChild -import trufflestg.data.DataTypes +import trufflestg.data.* +import trufflestg.panic +import trufflestg.todo +import java.lang.ArithmeticException import java.nio.ByteBuffer @@ -45,6 +44,15 @@ class StgPrim( // i think usually we need to whnf ourselves? @OptIn(ExperimentalUnsignedTypes::class) val primOps: Map StgPrimOp> = mapOf( + "gtChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x > y.x) 1L else 0L) }, + "geChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x >= y.x) 1L else 0L) }, + "eqChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x == y.x) 1L else 0L) }, + "neChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x != y.x) 1L else 0L) }, + "ltChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x < y.x) 1L else 0L) }, + "leChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x <= y.x) 1L else 0L) }, + + "ord#" to wrap1 { x: StgChar -> StgInt(x.x.toLong()) }, // TODO: should this be an unsigned conversion? + "catch#" to { object : StgPrimOp(3) { @field:Child var callWhnf1 = CallWhnf(1, false) @field:Child var callWhnf2 = CallWhnf(2, false) @@ -75,10 +83,17 @@ val primOps: Map StgPrimOp> = mapOf( ">=#" to wrap2 { x: StgInt, y: StgInt -> StgInt(if (x.x >= y.x) 1L else 0L) }, "==#" to wrap2 { x: StgInt, y: StgInt -> StgInt(if (x.x == y.x) 1L else 0L) }, + "andI#" to wrap2 { x: StgInt, y: StgInt -> StgInt(x.x and y.x) }, + "orI#" to wrap2 { x: StgInt, y: StgInt -> StgInt(x.x or y.x) }, + "xorI#" to wrap2 { x: StgInt, y: StgInt -> StgInt(x.x xor y.x) }, + "addIntC#" to wrap2 { x: StgInt, y: StgInt -> - // TODO: slow - try { UnboxedTuple(arrayOf(StgInt(Math.addExact(x.x, y.x)), StgInt(0L))) } - catch (e: ArithmeticException) { UnboxedTuple(arrayOf(StgInt(x.x + y.x), StgInt(1L))) } + val r = x.x + y.x + UnboxedTuple(arrayOf(StgInt(r), StgInt(if(x.x xor r and (y.x xor r) < 0) 1L else 0L))) + }, + "subIntC#" to wrap2 { x: StgInt, y: StgInt -> + val r = x.x - y.x + UnboxedTuple(arrayOf(StgInt(r), StgInt(if(x.x xor r and (y.x xor r) < 0) 1L else 0L))) }, "timesInt2#" to wrap2 { x: StgInt, y: StgInt -> @@ -134,11 +149,7 @@ val primOps: Map StgPrimOp> = mapOf( "readMutVar#" to wrap2 { x: StgMutVar, _: VoidInh -> UnboxedTuple(arrayOf(x.x)) }, "writeMutVar#" to wrap3 { x: StgMutVar, y: Any, v: VoidInh -> x.x = y; v }, - "leChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x <= y.x) 1L else 0L) }, - "gtChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x > y.x) 1L else 0L) }, - "geChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x >= y.x) 1L else 0L) }, - "ord#" to wrap1 { x: StgChar -> StgInt(x.x.toLong()) }, // TODO: should this be an unsigned conversion? - "chr#" to wrap1 { x: StgInt -> StgChar(x.x.toInt()) }, // TODO: ^ + "chr#" to wrap1 { x: StgInt -> StgChar(x.x.toInt()) }, // TODO: should this be an unsigned conversion? "int2Word#" to wrap1 { x: StgInt -> StgWord(x.x.toULong()) }, "word2Int#" to wrap1 { x: StgWord -> StgInt(x.x.toLong()) }, @@ -149,7 +160,6 @@ val primOps: Map StgPrimOp> = mapOf( "eqWord#" to wrap2 { x: StgWord, y: StgWord -> StgInt(if (x.x == y.x) 1L else 0L) }, "minusWord#" to wrap2 { x: StgWord, y: StgWord -> StgWord(x.x - y.x) }, - "eqChar#" to wrap2 { x: StgChar, y: StgChar -> StgInt(if (x.x == y.x) 1L else 0L) }, "tagToEnum#" to { object : StgPrimOp1() { // TODO: need to do this lazily because parent isn't set at initialization @@ -222,12 +232,22 @@ val primOps: Map StgPrimOp> = mapOf( "writeWideCharOffAddr#" to wrap4 { x: StgAddr, y: StgInt, z: StgChar, v: VoidInh -> x.arr.write(x.offset + 4*y.x.toInt(), z.x.toByteArray()); v }, + "writeWordArray#" to wrap4 { x: StgMutableByteArray, y: StgInt, z: StgWord, v: VoidInh -> + x.arr.write(8*y.toInt(), z.x.toLong()); v }, + + "indexWordArray#" to wrap2 { x: StgMutableByteArray, y: StgInt -> + StgWord(x.asBuffer().getLong(y.toInt()*8).toULong()) + }, + "plusAddr#" to wrap2 { x: StgAddr, y: StgInt -> StgAddr(x.arr, x.offset + y.x.toInt()) }, // TODO: make sure these are right "uncheckedIShiftL#" to wrap2 { x: StgInt, y: StgInt -> StgInt(x.x shl y.x.toInt()) }, - "uncheckedShiftRL#" to wrap2 { x: StgWord, y: StgInt -> StgWord(x.x shr y.x.toInt()) }, + "uncheckedIShiftRL#" to wrap2 { x: StgInt, y: StgInt -> StgInt(x.x ushr y.x.toInt()) }, + "uncheckedIShiftRA#" to wrap2 { x: StgInt, y: StgInt -> StgInt(x.x shr y.x.toInt()) }, "uncheckedShiftL#" to wrap2 { x: StgWord, y: StgInt -> StgWord(x.x shl y.x.toInt()) }, + "uncheckedShiftRL#" to wrap2 { x: StgWord, y: StgInt -> StgWord(x.x shr y.x.toInt()) }, + "or#" to wrap2 { x: StgWord, y: StgWord -> StgWord(x.x or y.x) }, // TODO? @@ -239,9 +259,12 @@ val primOps: Map StgPrimOp> = mapOf( override fun run(frame: VirtualFrame, args: Array): Any = callWhnf.execute(frame, args[0], arrayOf()) }}, - "raise#" to wrap1 { e: Any -> (throw HaskellException(e)) as VoidInh }, - "raiseIO#" to wrap2 { e: Any, _: VoidInh -> (throw HaskellException(e)) as VoidInh }, + "raise#" to wrap1 { e: Any -> throw HaskellException(e) }, + "raiseIO#" to wrap2 { e: Any, _: VoidInh -> throw HaskellException(e) }, + "newByteArray#" to wrap2 { x: StgInt, _: VoidInh -> + UnboxedTuple(arrayOf(StgMutableByteArray(ByteArray(x.toInt())))) + }, // TODO: actually pin & align? only matters if we want to use native code // FIXME: or for readAddrOffAddr, or if casted to Addr? "newAlignedPinnedByteArray#" to wrap3 { x: StgInt, alignment: StgInt, _: VoidInh -> @@ -251,7 +274,9 @@ val primOps: Map StgPrimOp> = mapOf( "unsafeFreezeByteArray#" to wrap2 { arr: StgMutableByteArray, _: VoidInh -> arr.frozen = true UnboxedTuple(arrayOf(arr)) }, - "byteArrayContents#" to wrap1 { arr: StgMutableByteArray -> StgAddr(arr.arr, 0) } + "byteArrayContents#" to wrap1 { arr: StgMutableByteArray -> StgAddr(arr.arr, 0) }, + + "sizeofByteArray#" to wrap1 { x: StgMutableByteArray -> StgInt(x.arr.size.toLong()) } ) diff --git a/src/trufflestg/stg/elab.kt b/src/trufflestg/stg/elab.kt index 2a0ae2d..f24dbfb 100644 --- a/src/trufflestg/stg/elab.kt +++ b/src/trufflestg/stg/elab.kt @@ -362,6 +362,7 @@ class Module( } }.associateBy { it.binder.binderId } + // TODO: build classes lazily val tyCons: Map val dataCons: Map init {