Skip to content

Commit

Permalink
refactoring - add a class to control the stack level
Browse files Browse the repository at this point in the history
  • Loading branch information
fglock committed Oct 22, 2024
1 parent 3d8db19 commit 1ceb682
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 39 deletions.
4 changes: 2 additions & 2 deletions src/main/java/org/perlonjava/codegen/EmitLiteral.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ static void emitList(EmitterVisitor emitterVisitor, ListNode node) {
mv.visitInsn(Opcodes.DUP);
// stack: [RuntimeList] [RuntimeList]

emitterVisitor.ctx.javaClassInfo.asmStackLevel += 2;
emitterVisitor.ctx.javaClassInfo.incrementStackLevel(2);;

// emit the list element
element.accept(emitterVisitor.with(RuntimeContextType.LIST));

emitterVisitor.ctx.javaClassInfo.asmStackLevel -= 2;
emitterVisitor.ctx.javaClassInfo.decrementStackLevel(2);;

// Call the add method to add the element to the RuntimeList
// This calls RuntimeDataProvider.addToList() in order to allow (1, 2, $x, @x, %x)
Expand Down
25 changes: 10 additions & 15 deletions src/main/java/org/perlonjava/codegen/EmitOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -596,14 +596,10 @@ static void handleNextOperator(EmitterContext ctx, OperatorNode node) {
throw new PerlCompilerException(node.tokenIndex, "Can't \"" + operator + "\" outside a loop block", ctx.errorUtil);
}

ctx.logDebug("visit(next): asmStackLevel: " + ctx.javaClassInfo.asmStackLevel);
ctx.logDebug("visit(next): asmStackLevel: " + ctx.javaClassInfo.stackLevelManager.getStackLevel());

int consumeStack = ctx.javaClassInfo.asmStackLevel;
int targetStack = loopLabels.asmStackLevel;
while (consumeStack-- > targetStack) {
// Consume the JVM stack.
ctx.mv.visitInsn(Opcodes.POP);
}
// Use StackLevelManager to emit POP instructions
ctx.javaClassInfo.stackLevelManager.emitPopInstructions(ctx.mv, loopLabels.asmStackLevel);

// Determine the appropriate label to jump to.
Label label = operator.equals("next") ? loopLabels.nextLabel
Expand All @@ -614,19 +610,18 @@ static void handleNextOperator(EmitterContext ctx, OperatorNode node) {

// Handles the 'return' operator, which exits a subroutine and returns a value.
static void handleReturnOperator(EmitterVisitor emitterVisitor, OperatorNode node) {
emitterVisitor.ctx.logDebug("visit(return) in context " + emitterVisitor.ctx.contextType);
emitterVisitor.ctx.logDebug("visit(return) will visit " + node.operand + " in context " + emitterVisitor.ctx.with(RuntimeContextType.RUNTIME).contextType);
EmitterContext ctx = emitterVisitor.ctx;

int consumeStack = emitterVisitor.ctx.javaClassInfo.asmStackLevel;
while (consumeStack-- > 0) {
// Consume the JVM stack.
emitterVisitor.ctx.mv.visitInsn(Opcodes.POP);
}
ctx.logDebug("visit(return) in context " + emitterVisitor.ctx.contextType);
ctx.logDebug("visit(return) will visit " + node.operand + " in context " + emitterVisitor.ctx.with(RuntimeContextType.RUNTIME).contextType);

// Use StackLevelManager to emit POP instructions
ctx.javaClassInfo.stackLevelManager.emitPopInstructions(ctx.mv, 0);

if (node.operand instanceof ListNode list) {
if (list.elements.size() == 1) {
// Special case for a list with 1 element.
list.elements.get(0).accept(emitterVisitor.with(RuntimeContextType.RUNTIME));
list.elements.getFirst().accept(emitterVisitor.with(RuntimeContextType.RUNTIME));
emitterVisitor.ctx.mv.visitJumpInsn(Opcodes.GOTO, emitterVisitor.ctx.javaClassInfo.returnLabel);
return;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/perlonjava/codegen/EmitStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) {
mv.visitInsn(Opcodes.POP); // we don't need the variable in the stack
// Stack: [iterator]

emitterVisitor.ctx.javaClassInfo.asmStackLevel += 1;
emitterVisitor.ctx.javaClassInfo.incrementStackLevel(1);;

// Add redo label
Label redoLabel = new Label();
Expand Down Expand Up @@ -226,7 +226,7 @@ static void emitFor1(EmitterVisitor emitterVisitor, For1Node node) {
// End of the loop
mv.visitLabel(loopEnd);

emitterVisitor.ctx.javaClassInfo.asmStackLevel -= 1;
emitterVisitor.ctx.javaClassInfo.decrementStackLevel(1);;

// Pop the iterator from the stack
mv.visitInsn(Opcodes.POP);
Expand Down
82 changes: 62 additions & 20 deletions src/main/java/org/perlonjava/codegen/JavaClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,109 @@
import java.util.ArrayDeque;
import java.util.Deque;

/**
* Represents information about a Java class being generated.
* This includes the class name, return label, stack level management,
* and a stack of loop labels for managing nested loops.
*/
public class JavaClassInfo {

/**
* The name of the Java class being generated.
*/
/** The name of the Java class. */
public String javaClassName;

/**
* The label to which the current method should return.
*/
/** The label to return to after method execution. */
public Label returnLabel;

/**
* ASM stack level
*/
public int asmStackLevel;
/** Manages the stack level for the class. */
public StackLevelManager stackLevelManager;

/**
* Stack to hold loop label information
*/
/** A stack of loop labels for managing nested loops. */
public Deque<LoopLabels> loopLabelStack;

/**
* Constructs a new JavaClassInfo object.
* Initializes the class name, stack level manager, and loop label stack.
*/
public JavaClassInfo() {
this.javaClassName = EmitterMethodCreator.generateClassName();
this.returnLabel = null;
this.asmStackLevel = 0;
this.stackLevelManager = new StackLevelManager();
this.loopLabelStack = new ArrayDeque<>();
}

/**
* Push a new LoopLabels object onto the stack.
* Pushes a new set of loop labels onto the loop label stack.
*
* @param labelName the name of the loop label
* @param nextLabel the label for the next iteration
* @param redoLabel the label for redoing the current iteration
* @param lastLabel the label for exiting the loop
*/
public void pushLoopLabels(String labelName, Label nextLabel, Label redoLabel, Label lastLabel) {
loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, asmStackLevel));
loopLabelStack.push(new LoopLabels(labelName, nextLabel, redoLabel, lastLabel, stackLevelManager.getStackLevel()));
}

/**
* Pop the top LoopLabels object off the stack.
* Pops the top set of loop labels from the loop label stack.
*/
public void popLoopLabels() {
loopLabelStack.pop();
}

/**
* Finds loop labels by their name.
*
* @param labelName the name of the loop label to find
* @return the LoopLabels object with the specified name, or the top of the stack if the name is null
*/
public LoopLabels findLoopLabelsByName(String labelName) {
if (labelName == null) {
return loopLabelStack.peek();
}
for (LoopLabels loopLabels : loopLabelStack) {
if (loopLabels.labelName.equals(labelName)) {
return loopLabels; // Found the matching label
return loopLabels;
}
}
return null; // Label not found
return null;
}

/**
* Increments the stack level by a specified amount.
*
* @param level the amount to increment the stack level by
*/
public void incrementStackLevel(int level) {
stackLevelManager.increment(level);
}

/**
* Decrements the stack level by a specified amount.
*
* @param level the amount to decrement the stack level by
*/
public void decrementStackLevel(int level) {
stackLevelManager.decrement(level);
}

/**
* Resets the stack level to its initial state.
*/
public void resetStackLevel() {
stackLevelManager.reset();
}

/**
* Returns a string representation of the JavaClassInfo object.
*
* @return a string representation of the JavaClassInfo object
*/
@Override
public String toString() {
return "JavaClassInfo{\n" +
" javaClassName='" + javaClassName + "',\n" +
" returnLabel=" + (returnLabel != null ? returnLabel.toString() : "null") + ",\n" +
" asmStackLevel=" + asmStackLevel + ",\n" +
" asmStackLevel=" + stackLevelManager.getStackLevel() + ",\n" +
" loopLabelStack=" + loopLabelStack + "\n" +
"}";
}
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/perlonjava/codegen/StackLevelManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.perlonjava.codegen;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class StackLevelManager {
private int stackLevel;

public StackLevelManager() {
this.stackLevel = 0;
}

public int getStackLevel() {
return stackLevel;
}

public void increment(int level) {
stackLevel += level;
}

public void decrement(int level) {
stackLevel -= level;
if (stackLevel < 0) {
stackLevel = 0;
}
}

public void reset() {
stackLevel = 0;
}

public void emitPopInstructions(MethodVisitor mv, int targetStackLevel) {
while (stackLevel > targetStackLevel) {
mv.visitInsn(Opcodes.POP);
decrement(1);
}
}
}

0 comments on commit 1ceb682

Please sign in to comment.