Skip to content

Commit

Permalink
more primops
Browse files Browse the repository at this point in the history
  • Loading branch information
acertain committed Apr 18, 2021
1 parent 8c1d336 commit 77b4b82
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 43 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions src/cbor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<KType,Array<KType?>> = Cache { it.arguments.map { x -> x.type }.toTypedArray() }
val paramsTypes: Cache<KCallable<*>, Array<KType>> = Cache { it.parameters.map { x -> x.type }.toTypedArray() }
val isEnumMap: Cache<Class<*>, Boolean> = Cache { Enum::class.java.isAssignableFrom(it) }
Expand Down
2 changes: 1 addition & 1 deletion src/trufflestg/data/AlgData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ class DynamicClassDataConInfo(
when (it) {
klass.name -> klass
else -> TODO(it)
}} as Class<DataFrameBuilder>
}}
builderKlass.constructors[0].newInstance() as DataFrameBuilder
}
}
Expand Down
1 change: 1 addition & 0 deletions src/trufflestg/data/Closure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
5 changes: 5 additions & 0 deletions src/trufflestg/data/stg_data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)?
Expand All @@ -67,6 +69,8 @@ class StgMutableByteArray(
@JvmField val arr: ByteArray
) {
@JvmField var frozen: Boolean = false

fun asBuffer(): ByteBuffer = ByteBuffer.wrap(arr)
}

//class StgByteArray(
Expand Down Expand Up @@ -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<Any>
Expand Down
70 changes: 46 additions & 24 deletions src/trufflestg/jit/StgFCall.kt
Original file line number Diff line number Diff line change
@@ -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<Arg>
Expand All @@ -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<String, () -> StgPrimOp> = mapOf(
// TODO: do something safer! (use x to store MessageDigest object?)
"__hsbase_MD5Init" to wrap2Boundary { x: StgAddr, y: VoidInh ->
Expand Down Expand Up @@ -104,6 +108,11 @@ val primFCalls: Map<String, () -> 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
Expand All @@ -117,7 +126,7 @@ val primFCalls: Map<String, () -> 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
Expand All @@ -136,10 +145,10 @@ val primFCalls: Map<String, () -> 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
Expand Down Expand Up @@ -197,6 +206,19 @@ val primFCalls: Map<String, () -> 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())))
}
)

Expand All @@ -213,4 +235,4 @@ inline fun <reified X, reified Y, reified Z, reified W : Any> wrap3Boundary(cros
}
inline fun <reified X, reified Y, reified Z, reified W : Any, reified A> 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)!!) }
}
}
61 changes: 43 additions & 18 deletions src/trufflestg/jit/StgPrim.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ 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
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


Expand Down Expand Up @@ -45,6 +44,15 @@ class StgPrim(
// i think usually we need to whnf ourselves?
@OptIn(ExperimentalUnsignedTypes::class)
val primOps: Map<String, () -> 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)
Expand Down Expand Up @@ -75,10 +83,17 @@ val primOps: Map<String, () -> 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 ->
Expand Down Expand Up @@ -134,11 +149,7 @@ val primOps: Map<String, () -> 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()) },
Expand All @@ -149,7 +160,6 @@ val primOps: Map<String, () -> 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
Expand Down Expand Up @@ -222,12 +232,22 @@ val primOps: Map<String, () -> 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?
Expand All @@ -239,9 +259,12 @@ val primOps: Map<String, () -> StgPrimOp> = mapOf(
override fun run(frame: VirtualFrame, args: Array<Any>): 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 ->
Expand All @@ -251,7 +274,9 @@ val primOps: Map<String, () -> 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()) }
)


Expand Down
1 change: 1 addition & 0 deletions src/trufflestg/stg/elab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ class Module(
}
}.associateBy { it.binder.binderId }

// TODO: build classes lazily
val tyCons: Map<Stg.TyConId, TyCon>
val dataCons: Map<Stg.DataConId, DataConInfo>
init {
Expand Down

0 comments on commit 77b4b82

Please sign in to comment.