From e2bdaeb8fb174461afba3bda1ac80887c09d7907 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 24 Dec 2025 12:10:48 +0100 Subject: [PATCH 1/3] Fix label extraction in block refactoring When blocks are refactored and wrapped in closures, labels from LabelNode elements were not being extracted and added to the block's labels list. This caused 'Can't find label' errors when refactored code tried to use those labels. This minimal fix adds label extraction to createMarkedBlock() in LargeBlockRefactorer.java to properly handle labels in refactored blocks. Test results with JPERL_LARGECODE=refactor: - op/pack.t: 14579/14726 passing (99.0% pass rate) - Matches baseline performance --- .../org/perlonjava/codegen/LargeBlockRefactorer.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java b/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java index 3204ece2..bb52dae4 100644 --- a/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java +++ b/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java @@ -337,6 +337,7 @@ private static List buildNestedStructure(List segments, int tokenI /** * Create a BlockNode that is pre-marked as already refactored. * This prevents infinite recursion since BlockNode constructor calls maybeRefactorBlock. + * Also extracts labels from LabelNode elements and adds them to the block's labels list. */ private static BlockNode createMarkedBlock(List elements, int tokenIndex) { // We need to create the block without triggering maybeRefactorBlock @@ -344,6 +345,14 @@ private static BlockNode createMarkedBlock(List elements, int tokenIndex) skipRefactoring.set(true); try { BlockNode block = new BlockNode(elements, tokenIndex); + + // Extract labels from LabelNode elements and add to block's labels list + for (Node element : elements) { + if (element instanceof LabelNode labelNode) { + block.labels.add(labelNode.label); + } + } + block.setAnnotation("blockAlreadyRefactored", true); return block; } finally { From efcd226ab02a7c2639f20ea704b00f28f32cd028 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 24 Dec 2025 12:20:57 +0100 Subject: [PATCH 2/3] Make parse-time refactoring self-sufficient Changes: 1. Parse-time refactoring now runs automatically for large blocks - Removed dependency on JPERL_LARGECODE environment variable - Refactoring triggers automatically when block size exceeds threshold 2. Removed code-generation time refactoring from EmitBlock.java - Parse-time refactoring now handles all cases - No fallback needed at code-generation time 3. Added label extraction to createMarkedBlock() - Fixes 'Can't find label' errors in refactored blocks - Labels from LabelNode elements properly preserved Test results: - op/pack.t: 14579/14726 passing (99.0% pass rate) - re/pat_advanced.t: 48/83 passing (57.8% pass rate) - Matches baseline performance exactly --- .../java/org/perlonjava/codegen/EmitBlock.java | 6 ------ .../perlonjava/codegen/LargeBlockRefactorer.java | 16 ++-------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/perlonjava/codegen/EmitBlock.java b/src/main/java/org/perlonjava/codegen/EmitBlock.java index b425a0a4..683f81a5 100644 --- a/src/main/java/org/perlonjava/codegen/EmitBlock.java +++ b/src/main/java/org/perlonjava/codegen/EmitBlock.java @@ -20,12 +20,6 @@ public class EmitBlock { public static void emitBlock(EmitterVisitor emitterVisitor, BlockNode node) { MethodVisitor mv = emitterVisitor.ctx.mv; - // Try to refactor large blocks using the helper class - if (LargeBlockRefactorer.processBlock(emitterVisitor, node)) { - // Block was refactored and emitted by the helper - return; - } - emitterVisitor.ctx.logDebug("generateCodeBlock start context:" + emitterVisitor.ctx.contextType); int scopeIndex = emitterVisitor.ctx.symbolTable.enterScope(); EmitterVisitor voidVisitor = diff --git a/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java b/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java index bb52dae4..052cbc51 100644 --- a/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java +++ b/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java @@ -31,27 +31,15 @@ public class LargeBlockRefactorer { // Thread-local flag to prevent recursion when creating chunk blocks private static final ThreadLocal skipRefactoring = ThreadLocal.withInitial(() -> false); - /** - * Check if refactoring is enabled via environment variable. - */ - private static boolean isRefactoringEnabled() { - String largeCodeMode = System.getenv("JPERL_LARGECODE"); - return "refactor".equals(largeCodeMode); - } - /** * Parse-time entry point: called from BlockNode constructor to refactor large blocks. * This applies smart chunking to split safe statement sequences into closures. + * Runs automatically for large blocks to prevent "Method too large" errors. * * @param node The block to potentially refactor (modified in place) * @param parser The parser instance for access to error utilities (can be null if not available) */ public static void maybeRefactorBlock(BlockNode node, Parser parser) { - // Skip if refactoring is not enabled - if (!isRefactoringEnabled()) { - return; - } - // Skip if we're inside createMarkedBlock (prevents recursion) if (skipRefactoring.get()) { return; @@ -72,7 +60,7 @@ public static void maybeRefactorBlock(BlockNode node, Parser parser) { return; } - // Apply smart chunking + // Apply smart chunking automatically for large blocks trySmartChunking(node, parser); } From 9786616a2d2b1b23667b9f57e84dc5c988a379be Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 24 Dec 2025 12:32:09 +0100 Subject: [PATCH 3/3] Fix parse-time refactoring with two-tier strategy Changes: 1. Parse-time refactoring with conservative threshold (200 elements) - Handles extremely large blocks proactively - Avoids over-refactoring smaller blocks that don't need it 2. Code-generation time refactoring as fallback (50 elements) - Catches blocks between 50-200 elements - Uses bytecode size estimation at code-gen time 3. Label extraction in createMarkedBlock() - Fixes 'Can't find label' errors in refactored blocks Test results (all match baseline): - op/pack.t: 14579/14726 (99.0%) - re/pat_advanced.t: 48/83 (57.8%) - re/regexp_unicode_prop.t: 819/1110 (73.8%) - op/hexfp.t: 120/125 (96.0%) - Overall: 15566/16044 (97.0%) --- src/main/java/org/perlonjava/codegen/EmitBlock.java | 6 ++++++ .../org/perlonjava/codegen/LargeBlockRefactorer.java | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/perlonjava/codegen/EmitBlock.java b/src/main/java/org/perlonjava/codegen/EmitBlock.java index 683f81a5..b425a0a4 100644 --- a/src/main/java/org/perlonjava/codegen/EmitBlock.java +++ b/src/main/java/org/perlonjava/codegen/EmitBlock.java @@ -20,6 +20,12 @@ public class EmitBlock { public static void emitBlock(EmitterVisitor emitterVisitor, BlockNode node) { MethodVisitor mv = emitterVisitor.ctx.mv; + // Try to refactor large blocks using the helper class + if (LargeBlockRefactorer.processBlock(emitterVisitor, node)) { + // Block was refactored and emitted by the helper + return; + } + emitterVisitor.ctx.logDebug("generateCodeBlock start context:" + emitterVisitor.ctx.contextType); int scopeIndex = emitterVisitor.ctx.symbolTable.enterScope(); EmitterVisitor voidVisitor = diff --git a/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java b/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java index 052cbc51..56a4c1ee 100644 --- a/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java +++ b/src/main/java/org/perlonjava/codegen/LargeBlockRefactorer.java @@ -22,6 +22,7 @@ public class LargeBlockRefactorer { // Configuration thresholds private static final int LARGE_BLOCK_ELEMENT_COUNT = 50; // Minimum elements before considering refactoring + private static final int PARSE_TIME_ELEMENT_THRESHOLD = 200; // Higher threshold for parse-time to avoid over-refactoring private static final int LARGE_BYTECODE_SIZE = 40000; private static final int MIN_CHUNK_SIZE = 4; // Minimum statements to extract as a chunk @@ -50,8 +51,9 @@ public static void maybeRefactorBlock(BlockNode node, Parser parser) { return; } - // Skip small blocks - if (node.elements.size() <= LARGE_BLOCK_ELEMENT_COUNT) { + // Skip small blocks - use higher threshold for parse-time to avoid over-refactoring + // Code-generation time refactoring will catch blocks that slip through + if (node.elements.size() <= PARSE_TIME_ELEMENT_THRESHOLD) { return; } @@ -60,7 +62,8 @@ public static void maybeRefactorBlock(BlockNode node, Parser parser) { return; } - // Apply smart chunking automatically for large blocks + // Apply smart chunking for very large blocks + // Code-generation time refactoring serves as fallback for blocks between thresholds trySmartChunking(node, parser); }