Fast-path bytecode statement execution
This commit is contained in:
parent
a61b5a31be
commit
fc7d26ee4b
@ -28,6 +28,7 @@ internal suspend fun executeBytecodeWithSeed(scope: Scope, stmt: Statement, labe
|
|||||||
else -> null
|
else -> null
|
||||||
} ?: scope.raiseIllegalState("$label requires bytecode statement")
|
} ?: scope.raiseIllegalState("$label requires bytecode statement")
|
||||||
scope.pos = bytecode.pos
|
scope.pos = bytecode.pos
|
||||||
|
bytecode.callOnFast(scope)?.let { return it }
|
||||||
return CmdVm().execute(bytecode.bytecodeFunction(), scope, scope.args) { frame, _ ->
|
return CmdVm().execute(bytecode.bytecodeFunction(), scope, scope.args) { frame, _ ->
|
||||||
seedFrameLocalsFromScope(frame, scope)
|
seedFrameLocalsFromScope(frame, scope)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,19 +23,36 @@ import net.sergeych.lyng.obj.*
|
|||||||
class BytecodeStatement private constructor(
|
class BytecodeStatement private constructor(
|
||||||
val original: Statement,
|
val original: Statement,
|
||||||
private val function: CmdFunction,
|
private val function: CmdFunction,
|
||||||
) : Statement(original.isStaticConst, original.isConst, original.returnType) {
|
) : Statement(original.isStaticConst, original.isConst, original.returnType), BytecodeCallable {
|
||||||
override val pos: Pos = original.pos
|
override val pos: Pos = original.pos
|
||||||
|
|
||||||
|
private val declaredLocalNames: Set<String> by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
function.constants
|
||||||
|
.mapNotNull { it as? BytecodeConst.LocalDecl }
|
||||||
|
.mapTo(mutableSetOf()) { it.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun callOnFast(scope: Scope): Obj? {
|
||||||
|
scope.pos = pos
|
||||||
|
if (!function.fastOnly) return null
|
||||||
|
if (!canFastSeedUndeclaredLocals(function, declaredLocalNames, emptySet())) return null
|
||||||
|
val binder: (CmdFrame, Arguments) -> Unit = { frame, _ ->
|
||||||
|
if (!trySeedFrameLocalsFromScopeFast(frame, scope)) {
|
||||||
|
scope.raiseIllegalState("fast local seeding is not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CmdVm().executeFastOnlyNoSuspend(function, scope, scope.args, binder)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
scope.pos = pos
|
scope.pos = pos
|
||||||
val declaredNames = function.constants
|
val fastResult = callOnFast(scope)
|
||||||
.mapNotNull { it as? BytecodeConst.LocalDecl }
|
if (fastResult != null) return fastResult
|
||||||
.mapTo(mutableSetOf()) { it.name }
|
|
||||||
val binder: suspend (CmdFrame, Arguments) -> Unit = { frame, _ ->
|
val binder: suspend (CmdFrame, Arguments) -> Unit = { frame, _ ->
|
||||||
val localNames = frame.fn.localSlotNames
|
val localNames = frame.fn.localSlotNames
|
||||||
for (i in localNames.indices) {
|
for (i in localNames.indices) {
|
||||||
val name = localNames[i] ?: continue
|
val name = localNames[i] ?: continue
|
||||||
if (declaredNames.contains(name)) continue
|
if (declaredLocalNames.contains(name)) continue
|
||||||
val slotType = frame.getLocalSlotTypeCode(i)
|
val slotType = frame.getLocalSlotTypeCode(i)
|
||||||
if (slotType != SlotType.UNKNOWN.code && slotType != SlotType.OBJ.code) {
|
if (slotType != SlotType.UNKNOWN.code && slotType != SlotType.OBJ.code) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -17,7 +17,11 @@
|
|||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
|
import net.sergeych.lyng.FrameSlotRef
|
||||||
|
import net.sergeych.lyng.RecordSlotRef
|
||||||
|
import net.sergeych.lyng.ScopeSlotRef
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
import net.sergeych.lyng.obj.ObjProperty
|
||||||
|
|
||||||
internal fun canFastSeedUndeclaredLocals(
|
internal fun canFastSeedUndeclaredLocals(
|
||||||
fn: CmdFunction,
|
fn: CmdFunction,
|
||||||
@ -35,6 +39,37 @@ internal fun canFastSeedUndeclaredLocals(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun trySeedFrameLocalsFromScopeFast(frame: CmdFrame, scope: Scope): Boolean {
|
||||||
|
val localNames = frame.fn.localSlotNames
|
||||||
|
if (localNames.isEmpty()) return true
|
||||||
|
val base = frame.fn.scopeSlotCount
|
||||||
|
for (i in localNames.indices) {
|
||||||
|
val name = localNames[i] ?: continue
|
||||||
|
val slotType = frame.getLocalSlotTypeCode(i)
|
||||||
|
if (slotType != SlotType.UNKNOWN.code && slotType != SlotType.OBJ.code) continue
|
||||||
|
if (slotType == SlotType.OBJ.code && frame.frame.getRawObj(i) != null) continue
|
||||||
|
val record = scope.getLocalRecordDirect(name)
|
||||||
|
?: scope.chainLookupIgnoreClosure(name, followClosure = true)
|
||||||
|
?: continue
|
||||||
|
val value = when {
|
||||||
|
record.type == ObjRecord.Type.Delegated -> return false
|
||||||
|
record.type == ObjRecord.Type.Property -> return false
|
||||||
|
record.value is ObjProperty -> return false
|
||||||
|
else -> when (val direct = record.value) {
|
||||||
|
is FrameSlotRef -> direct.resolvedCaptureValueOrNull() ?: return false
|
||||||
|
is RecordSlotRef -> direct.resolvedCaptureValueOrNull() ?: return false
|
||||||
|
is ScopeSlotRef -> direct.resolvedCaptureValueOrNull() ?: return false
|
||||||
|
else -> direct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value is FrameSlotRef && value.refersTo(frame.frame, i)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
frame.setObjUnchecked(base + i, value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) {
|
internal suspend fun seedFrameLocalsFromScope(frame: CmdFrame, scope: Scope) {
|
||||||
val localNames = frame.fn.localSlotNames
|
val localNames = frame.fn.localSlotNames
|
||||||
if (localNames.isEmpty()) return
|
if (localNames.isEmpty()) return
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user