From 6efdfc1964fb310b43d901ae39913b23bb737ba6 Mon Sep 17 00:00:00 2001 From: sergeych Date: Wed, 11 Feb 2026 21:02:48 +0300 Subject: [PATCH] Step 26C: remove forceScopeSlots --- bytecode_migration_plan.md | 2 +- .../lyng/bytecode/BytecodeCompiler.kt | 111 +++--------------- 2 files changed, 16 insertions(+), 97 deletions(-) diff --git a/bytecode_migration_plan.md b/bytecode_migration_plan.md index 7d31a9c..7f04200 100644 --- a/bytecode_migration_plan.md +++ b/bytecode_migration_plan.md @@ -123,7 +123,7 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te - [ ] Step 26: Bytecode-backed lambdas (remove `ValueFnRef` runtime execution). - [x] Compile lambda bodies to bytecode and emit an opcode to create a callable from bytecode + capture plan. - [x] Remove `containsValueFnRef` helper now that lambdas are bytecode-backed. - - [ ] Remove `forceScopeSlots` branches once no bytecode paths depend on scope slots. + - [x] Remove `forceScopeSlots` branches once no bytecode paths depend on scope slots. - [x] Add JVM tests for captured locals and delegated locals inside lambdas on the bytecode path. - [ ] Step 27: Remove interpreter opcodes and constants from bytecode runtime. - [ ] Delete `BytecodeConst.ValueFn`, `CmdMakeValueFn`, and `MAKE_VALUE_FN`. diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt index 3376709..c85c1f8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -70,7 +70,6 @@ class BytecodeCompiler( private val intLoopVarNames = LinkedHashSet() private val valueFnRefs = LinkedHashSet() private val loopStack = ArrayDeque() - private var forceScopeSlots = false private var currentPos: Pos? = null private data class LoopContext( @@ -372,7 +371,6 @@ class BytecodeCompiler( return CompiledValue(slot, resolved) } if (allowLocalSlots) { - if (!forceScopeSlots) { val localIndex = localSlotIndexByName[ref.name] if (localIndex != null) { val slot = scopeSlotCount + localIndex @@ -383,13 +381,6 @@ class BytecodeCompiler( val resolved = slotTypes[slot] ?: SlotType.UNKNOWN return CompiledValue(slot, resolved) } - } - if (forceScopeSlots) { - scopeSlotIndexByName[ref.name]?.let { slot -> - val resolved = slotTypes[slot] ?: SlotType.UNKNOWN - return CompiledValue(slot, resolved) - } - } } null } @@ -402,7 +393,6 @@ class BytecodeCompiler( return CompiledValue(slot, resolved) } if (allowLocalSlots) { - if (!forceScopeSlots) { val localIndex = localSlotIndexByName[ref.name] if (localIndex != null) { val slot = scopeSlotCount + localIndex @@ -413,13 +403,6 @@ class BytecodeCompiler( val resolved = slotTypes[slot] ?: SlotType.UNKNOWN return CompiledValue(slot, resolved) } - } - if (forceScopeSlots) { - scopeSlotIndexByName[ref.name]?.let { slot -> - val resolved = slotTypes[slot] ?: SlotType.UNKNOWN - return CompiledValue(slot, resolved) - } - } } null } @@ -3620,22 +3603,15 @@ class BytecodeCompiler( return CompiledValue(slot, resolved) } if (!allowLocalSlots) return null - if (!forceScopeSlots) { - scopeSlotIndexByName[name]?.let { slot -> - val resolved = slotTypes[slot] ?: SlotType.UNKNOWN - return CompiledValue(slot, resolved) - } - localSlotIndexByName[name]?.let { localIndex -> - val slot = scopeSlotCount + localIndex - val resolved = slotTypes[slot] ?: SlotType.UNKNOWN - return CompiledValue(slot, resolved) - } - return null - } scopeSlotIndexByName[name]?.let { slot -> val resolved = slotTypes[slot] ?: SlotType.UNKNOWN return CompiledValue(slot, resolved) } + localSlotIndexByName[name]?.let { localIndex -> + val slot = scopeSlotCount + localIndex + val resolved = slotTypes[slot] ?: SlotType.UNKNOWN + return CompiledValue(slot, resolved) + } return null } @@ -4560,7 +4536,7 @@ class BytecodeCompiler( emitInlineStatements(stmt.statements(), needResult) private fun shouldInlineBlock(stmt: BlockStatement): Boolean { - return allowLocalSlots && !forceScopeSlots + return allowLocalSlots } private fun compileInlineBlock(name: String, stmt: net.sergeych.lyng.InlineBlockStatement): CmdFunction? { @@ -4585,7 +4561,7 @@ class BytecodeCompiler( private fun compileLoopBody(stmt: Statement, needResult: Boolean): CompiledValue? { val target = if (stmt is BytecodeStatement) stmt.original else stmt if (target is BlockStatement) { - val useInline = !forceScopeSlots && target.slotPlan.isEmpty() && target.captureSlots.isEmpty() + val useInline = target.slotPlan.isEmpty() && target.captureSlots.isEmpty() return if (useInline) emitInlineBlock(target, needResult) else emitBlock(target, needResult) } return compileStatementValueOrFallback(target, needResult) @@ -4650,7 +4626,7 @@ class BytecodeCompiler( updateSlotObjClass(localSlot, stmt.initializer, stmt.initializerObjClass) updateNameObjClassFromSlot(stmt.name, localSlot) val shadowedScopeSlot = scopeSlotIndexByName.containsKey(stmt.name) - if (forceScopeSlots || !shadowedScopeSlot) { + if (!shadowedScopeSlot) { val declId = builder.addConst( BytecodeConst.LocalDecl( stmt.name, @@ -4867,14 +4843,14 @@ class BytecodeCompiler( slot } var emitDeclLocal = usedOverride - if (useLoopScope && !forceScopeSlots) { + if (useLoopScope) { val loopVarOnly = loopSlotPlan.size == 1 && loopSlotPlan.containsKey(stmt.loopVarName) val loopVarIsLocal = loopSlotId >= scopeSlotCount if (loopVarOnly && loopVarIsLocal) { useLoopScope = false } } - if (useLoopScope && allowLocalSlots && !forceScopeSlots) { + if (useLoopScope && allowLocalSlots) { val needsScope = allowedScopeNames?.let { names -> loopSlotPlan.keys.any { names.contains(it) } } == true @@ -4883,7 +4859,7 @@ class BytecodeCompiler( } } emitDeclLocal = emitDeclLocal && useLoopScope - if (!forceScopeSlots && loopSlotId < scopeSlotCount) { + if (loopSlotId < scopeSlotCount) { val localSlot = allocSlot() loopSlotOverrides[stmt.loopVarName] = localSlot usedOverride = true @@ -5738,7 +5714,7 @@ class BytecodeCompiler( val loopKeys = loopSlotOverrides.keys.sorted().joinToString(prefix = "[", postfix = "]") val localKeys = localSlotIndexByName.keys.sorted().joinToString(prefix = "[", postfix = "]") val scopeKeys = scopeSlotIndexByName.keys.sorted().joinToString(prefix = "[", postfix = "]") - val info = " ref=$refKind loopSlots=$loopKeys localSlots=$localKeys scopeSlots=$scopeKeys forceScopeSlots=$forceScopeSlots" + val info = " ref=$refKind loopSlots=$loopKeys localSlots=$localKeys scopeSlots=$scopeKeys" throw BytecodeCompileException("Unresolved name '$name'.$info", pos) } val refInfo = when (ref) { @@ -6169,10 +6145,6 @@ class BytecodeCompiler( val nameIndex = localSlotIndexByName[ref.name] if (nameIndex != null) return scopeSlotCount + nameIndex } - if (forceScopeSlots) { - val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) - return scopeSlotMap[scopeKey] - } val localKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) val localIndex = localSlotIndexByKey[localKey] if (localIndex != null) return scopeSlotCount + localIndex @@ -6223,7 +6195,6 @@ class BytecodeCompiler( valueFnRefs.clear() addrSlotByScopeSlot.clear() loopStack.clear() - forceScopeSlots = false if (slotTypeByScopeId.isNotEmpty()) { for ((scopeId, slots) in slotTypeByScopeId) { for ((slotIndex, cls) in slots) { @@ -6366,7 +6337,7 @@ class BytecodeCompiler( } } val isModuleSlot = isModuleSlot(scopeId, stmt.name) - if (allowLocalSlots && !forceScopeSlots && slotIndex != null && !isModuleSlot) { + if (allowLocalSlots && slotIndex != null && !isModuleSlot) { val key = ScopeSlotKey(scopeId, slotIndex) declaredLocalKeys.add(key) if (!localSlotInfoMap.containsKey(key)) { @@ -6463,56 +6434,6 @@ class BytecodeCompiler( collectLoopSlotPlans(stmt.original, scopeDepth) return } - if (forceScopeSlots) { - when (stmt) { - is net.sergeych.lyng.ForInStatement -> { - collectLoopSlotPlans(stmt.source, scopeDepth) - val loopDepth = scopeDepth + 1 - collectLoopSlotPlans(stmt.body, loopDepth) - stmt.elseStatement?.let { collectLoopSlotPlans(it, loopDepth) } - } - is net.sergeych.lyng.WhileStatement -> { - collectLoopSlotPlans(stmt.condition, scopeDepth) - val loopDepth = scopeDepth + 1 - collectLoopSlotPlans(stmt.body, loopDepth) - stmt.elseStatement?.let { collectLoopSlotPlans(it, loopDepth) } - } - is net.sergeych.lyng.DoWhileStatement -> { - val loopDepth = scopeDepth + 1 - collectLoopSlotPlans(stmt.body, loopDepth) - collectLoopSlotPlans(stmt.condition, loopDepth) - stmt.elseStatement?.let { collectLoopSlotPlans(it, loopDepth) } - } - is BlockStatement -> { - val nextDepth = scopeDepth + 1 - for (child in stmt.statements()) { - collectLoopSlotPlans(child, nextDepth) - } - } - is net.sergeych.lyng.InlineBlockStatement -> { - for (child in stmt.statements()) { - collectLoopSlotPlans(child, scopeDepth) - } - } - is IfStatement -> { - collectLoopSlotPlans(stmt.condition, scopeDepth) - collectLoopSlotPlans(stmt.ifBody, scopeDepth) - stmt.elseBody?.let { collectLoopSlotPlans(it, scopeDepth) } - } - is VarDeclStatement -> { - stmt.initializer?.let { collectLoopSlotPlans(it, scopeDepth) } - } - is ExpressionStatement -> {} - is net.sergeych.lyng.ReturnStatement -> { - stmt.resultExpr?.let { collectLoopSlotPlans(it, scopeDepth) } - } - is net.sergeych.lyng.ThrowStatement -> { - collectLoopSlotPlans(stmt.throwExpr, scopeDepth) - } - else -> {} - } - return - } when (stmt) { is net.sergeych.lyng.ForInStatement -> { collectLoopSlotPlans(stmt.source, scopeDepth) @@ -6682,9 +6603,8 @@ class BytecodeCompiler( captureSlotKeys.add(key) return } - val shouldLocalize = ref.isDelegated || !forceScopeSlots || intLoopVarNames.contains(ref.name) val isModuleSlot = if (ref.isDelegated) false else isModuleSlot(scopeId, ref.name) - if (allowLocalSlots && shouldLocalize && !isModuleSlot) { + if (allowLocalSlots && !isModuleSlot) { if (!localSlotInfoMap.containsKey(key)) { localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated) } @@ -6731,9 +6651,8 @@ class BytecodeCompiler( } captureSlotKeys.add(key) } else { - val shouldLocalize = target.isDelegated || !forceScopeSlots || intLoopVarNames.contains(target.name) val isModuleSlot = if (target.isDelegated) false else isModuleSlot(scopeId, target.name) - if (allowLocalSlots && shouldLocalize && !isModuleSlot) { + if (allowLocalSlots && !isModuleSlot) { if (!localSlotInfoMap.containsKey(key)) { localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated) }