Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ private static List<Node> createNestedListClosures(List<Node> chunks, int tokenI
* @return true if the list exceeds size thresholds and should be refactored
*/
private static boolean shouldRefactor(List<Node> elements) {
// Use sampling to estimate bytecode size - avoid O(n) traversal
// Estimate bytecode size by visiting all elements (no sampling)
// Sampling was causing inaccurate estimates for mixed element types
int n = elements.size();
if (n == 0) {
return false;
Expand All @@ -160,14 +161,13 @@ private static boolean shouldRefactor(List<Node> elements) {
return size > LARGE_BYTECODE_SIZE;
}

int sampleSize = Math.min(10, n);
long totalSampleSize = 0;
for (int i = 0; i < sampleSize; i++) {
int index = (int) (((long) i * (n - 1)) / (sampleSize - 1));
totalSampleSize += BytecodeSizeEstimator.estimateSnippetSize(elements.get(index));
// Estimate all elements for accurate size calculation
long totalSize = 0;
for (Node element : elements) {
totalSize += BytecodeSizeEstimator.estimateSnippetSize(element);
}
long estimatedTotalSize = (totalSampleSize * n) / sampleSize;
return estimatedTotalSize > LARGE_BYTECODE_SIZE;

return totalSize > LARGE_BYTECODE_SIZE;
}

/**
Expand Down
27 changes: 21 additions & 6 deletions src/main/java/org/perlonjava/astvisitor/BytecodeSizeEstimator.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@ public void visit(NumberNode node) {

@Override
public void visit(StringNode node) {
// String literals: LDC + object creation = LDC_INSTRUCTION + OBJECT_CREATION (10 bytes)
int stringSize = LDC_INSTRUCTION + OBJECT_CREATION;
estimatedSize += stringSize;
// String literals: Based on actual disassembly showing:
// LDC <constant> (2-3 bytes for constant pool index)
// INVOKESTATIC (3 bytes) - getScalarByteString or similar
// Total: 5-6 bytes per string
estimatedSize += LDC_INSTRUCTION + INVOKE_STATIC; // 3 + 3 = 6 bytes
}

@Override
Expand Down Expand Up @@ -216,12 +218,25 @@ public void visit(BlockNode node) {

@Override
public void visit(ListNode node) {
// Mirror EmitLiteral.emitList() patterns
estimatedSize += OBJECT_CREATION; // Create RuntimeList
// Mirror EmitLiteral.emitList() patterns in LIST context
// Based on actual disassembly: each element requires DUP + element evaluation + add

estimatedSize += OBJECT_CREATION; // Create RuntimeList (NEW + DUP + INVOKESPECIAL = 7 bytes)

for (Node element : node.elements) {
// Per-element list overhead (DUP + add call)
estimatedSize += DUP_INSTRUCTION; // Duplicate RuntimeList reference (1 byte)
estimatedSize += METHOD_CALL_OVERHEAD; // RuntimeList.add() call (4 bytes)

// Let the element estimate itself via visitor pattern
element.accept(this);
estimatedSize += METHOD_CALL_OVERHEAD; // Add to list
}

// Constant pool overhead for large lists
// When constant pool grows beyond 256 entries, LDC becomes LDC_W (3 bytes instead of 2)
if (node.elements.size() > 200) {
// Large constant pool: LDC_W costs 3 bytes instead of 2
estimatedSize += node.elements.size(); // +1 byte per element for LDC_W
}
}

Expand Down