Class AbstractControlFlowAsmGenerator
- Direct Known Subclasses:
DoWhileLoopAsmGenerator, ForRangePolymorphicAsmGenerator, GuardedAssignmentAsmGenerator, IfElseAsmGenerator, LogicalOperationAsmGenerator, QuestionOperatorAsmGenerator, SwitchAsmGenerator, TryCatchAsmGenerator, WhileLoopAsmGenerator
All control flow generators (question operator, if/else, switch, loops, try/catch) extend this class to reuse common logic and ensure consistent stack frame handling.
Stack Frame Invariant: All helper methods maintain the invariant that the JVM operand stack is empty before and after their execution. Results are always stored in local variables, never left on the stack. This ensures correct stack frame balancing at control flow merge points.
Label Naming Convention (MANDATORY)
ALL control flow generators MUST use scopeId from IR instructions for label uniqueness.
NEVER use result variables, loop variable names, or any other identifiers for labels.
// CORRECT - uses scopeId from IR instruction
final var label = createControlFlowLabel("while_start", instr.getScopeId());
final var label = createControlFlowLabel("for_asc", scopeMetadata.loopScopeId());
final var label = createControlFlowLabel("if_next", conditionCase.caseScopeId());
// WRONG - uses result variable (fragile, inconsistent)
final var label = createControlFlowLabel("prefix", instr.getResult());
// WRONG - uses variable name (collides in nested loops)
final var label = createControlFlowLabel("for_asc", loopVariableName);
Rationale:
- Guaranteed Uniqueness: Scope IDs are globally unique per method by IR generator contract
- Semantic Clarity: Scope IDs represent lexical structure, not operational details
- Consistency: All loop and conditional generators use this pattern
- Robustness: Immune to IR optimizations that might reuse temp variables
- Nested Safety: Each nested scope gets unique ID, preventing label collisions
For Nested Constructs:
- Nested loops with same variable name (e.g., "for i" inside "for i"): Each has unique loopScopeId
- Nested conditions: Each branch has unique caseScopeId
- No collisions possible, even with identical variable names
See LABEL_NAMING_CONVENTION.md for complete documentation and examples.
-
Nested Class Summary
Nested classes/interfaces inherited from class AbstractAsmGenerator
AbstractAsmGenerator.LocalVariableInfo, AbstractAsmGenerator.MethodContext, AbstractAsmGenerator.ScopeInfo, AbstractAsmGenerator.TempVariableSource -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected final BytecodeGenerationContextContext stack for nested control flow.Fields inherited from class AbstractAsmGenerator
classWriter, constructTargetTuple, outputVisitor -
Constructor Summary
ConstructorsModifierConstructorDescriptionprotectedAbstractControlFlowAsmGenerator(ConstructTargetTuple constructTargetTuple, OutputVisitor outputVisitor, org.objectweb.asm.ClassWriter classWriter, BytecodeGenerationContext context) -
Method Summary
Modifier and TypeMethodDescriptionprotected voidbranchIfFalse(String primitiveCondition, org.objectweb.asm.Label falseLabel) Load primitive condition (int) and branch if false (0).protected voidbranchIfTrue(String primitiveCondition, org.objectweb.asm.Label trueLabel) Load primitive condition (int) and branch if true (non-zero).protected voidcopyResultVariable(String sourceVar, String destVar) Copy result from source variable to destination variable (object reference).protected org.objectweb.asm.LabelcreateControlFlowLabel(String prefix, String uniqueId) Generate unique label for control flow construct.protected voidjumpTo(org.objectweb.asm.Label label) Generate unconditional jump to label.protected voidplaceLabel(org.objectweb.asm.Label label) Place label at current bytecode position.protected voidprocessBodyEvaluation(List<IRInstr> bodyEvaluation) Process body evaluation instructions via recursive visiting.protected voidprocessConditionCase(ConditionCaseDetails conditionCase, org.objectweb.asm.Label nextCaseLabel, org.objectweb.asm.Label endLabel, String resultVar) Process a condition case: evaluate condition, branch, execute body.protected voidprocessConditionCaseWithoutLabelPlacement(ConditionCaseDetails conditionCase, org.objectweb.asm.Label nextCaseLabel, org.objectweb.asm.Label endLabel, String resultVar) Process a condition case WITHOUT placing the nextCaseLabel.protected voidprocessConditionEvaluation(List<IRInstr> conditionEvaluation) Process condition evaluation instructions via recursive visiting.protected voidprocessInstructions(List<IRInstr> instructions) Process list of IR instructions via recursive delegation to OutputVisitor.Methods inherited from class AbstractAsmGenerator
convertBooleanToInt, convertToJvmDescriptor, convertToJvmName, generateBooleanLiteral, generateCharacterLiteral, generateDebugInfo, generateFloatLiteral, generateIntegerLiteral, generateLoadVariable, generateLocalVariableTable, generateMethodDescriptor, generateObjectLiteral, generateStackOperation, generateStoreVariable, generateStringLiteral, getCurrentMethodVisitor, getMethodContext, getOrCreateLabel, getVariableIndex, isVariableAllocated, setCurrentMethodVisitor, setSharedMethodContext, trackTempVariableFromLiteral, trackTempVariableFromLoad
-
Field Details
-
context
Context stack for nested control flow. Allows nested generators to query enclosing control flow context.
-
-
Constructor Details
-
AbstractControlFlowAsmGenerator
protected AbstractControlFlowAsmGenerator(ConstructTargetTuple constructTargetTuple, OutputVisitor outputVisitor, org.objectweb.asm.ClassWriter classWriter, BytecodeGenerationContext context)
-
-
Method Details
-
createControlFlowLabel
Generate unique label for control flow construct. Pattern: prefix_[dispatchCase_]scopeIdCRITICAL: Always use
scopeIdas the uniqueId parameter.FOR_RANGE Dispatch: When inside FOR_RANGE dispatch case, labels include dispatch case suffix to ensure uniqueness when same body IR is processed three times:
// Outside FOR_RANGE: if_next_scope_5 // Equal case: if_next_equal_scope_5 // Ascending case: if_next_ascending_scope_5 // Descending case: if_next_descending_scope_5
// CORRECT - main control flow structure createControlFlowLabel("while_start", instr.getScopeId()); createControlFlowLabel("for_asc", scopeMetadata.loopScopeId()); // CORRECT - condition cases createControlFlowLabel("if_next", conditionCase.caseScopeId()); // WRONG - uses result variable (fragile) createControlFlowLabel("prefix", instr.getResult()); // WRONG - uses variable name (collides in nested loops) createControlFlowLabel("for_asc", loopVariableName);Why scopeId?
- Globally unique per method (guaranteed by IR generator)
- Semantically appropriate (represents lexical scope)
- Consistent across all control flow generators
- Robust to IR refactoring (e.g., temp variable reuse)
- Safe for nested constructs with same variable names
- Parameters:
prefix- Label prefix identifying the construct type (e.g., "while_start", "if_end", "for_asc")uniqueId- MUST be scopeId from IR instruction (never use result variable or variable name)- Returns:
- JVM Label for bytecode generation
-
processInstructions
Process list of IR instructions via recursive delegation to OutputVisitor.Each instruction knows how to generate its own bytecode via accept(visitor). This enables polymorphic dispatch to the correct generator:
- CALL instructions → CallInstrAsmGenerator
- LOAD/STORE/RETAIN/RELEASE → MemoryInstrAsmGenerator
- LOAD_LITERAL → LiteralInstrAsmGenerator
- ASSERT → BranchInstrAsmGenerator
- SCOPE_ENTER/EXIT → ScopeInstrAsmGenerator
- CONTROL_FLOW_CHAIN → ControlFlowChainAsmGenerator
All instructions maintain stack-empty invariant.
Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty (all results in local variables)
- Parameters:
instructions- List of IR instructions to process
-
processConditionEvaluation
Process condition evaluation instructions via recursive visiting. Semantic wrapper aroundprocessInstructions(java.util.List)for clarity.Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty (all results in local variables)
- Parameters:
conditionEvaluation- List of IR instructions to evaluate condition
-
processBodyEvaluation
Process body evaluation instructions via recursive visiting. Semantic wrapper aroundprocessInstructions(java.util.List)for clarity.Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty (all results in local variables)
- Parameters:
bodyEvaluation- List of IR instructions for case/branch body
-
branchIfFalse
Load primitive condition (int) and branch if false (0).Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty (IFEQ consumes the int)
Control Flow: If condition is 0 (false), jumps to falseLabel. Otherwise, execution continues to next instruction.
- Parameters:
primitiveCondition- Variable name holding primitive int (0 or 1)falseLabel- Label to jump to if condition is false (0)
-
branchIfTrue
Load primitive condition (int) and branch if true (non-zero).Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty (IFNE consumes the int)
Control Flow: If condition is non-zero (true), jumps to trueLabel. Otherwise, execution continues to next instruction.
Used by logical OR operator for short-circuit optimization: If left operand is true, skip right evaluation and return left.
- Parameters:
primitiveCondition- Variable name holding primitive int (0 or 1)trueLabel- Label to jump to if condition is true (non-zero)
-
copyResultVariable
Copy result from source variable to destination variable (object reference).Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty
Bytecode: ALOAD source_index, ASTORE dest_index
- Parameters:
sourceVar- Variable holding the result to copydestVar- Variable to store the result into
-
jumpTo
protected void jumpTo(org.objectweb.asm.Label label) Generate unconditional jump to label.Stack Frame Invariant: Pre-condition: stack is empty Post-condition: control transferred (stack irrelevant after GOTO)
Control Flow: Execution continues at the target label. Stack at target must match stack at GOTO (empty in our convention).
- Parameters:
label- Target label for unconditional jump
-
placeLabel
protected void placeLabel(org.objectweb.asm.Label label) Place label at current bytecode position.Stack Frame Invariant: Pre-condition: stack state depends on incoming control flow paths Post-condition: stack state unchanged (label is just a position marker)
CRITICAL: All control flow paths reaching this label MUST have the same stack depth. In our convention, all paths arrive with empty stack.
- Parameters:
label- Label to place at current position
-
processConditionCase
protected void processConditionCase(ConditionCaseDetails conditionCase, org.objectweb.asm.Label nextCaseLabel, org.objectweb.asm.Label endLabel, String resultVar) Process a condition case: evaluate condition, branch, execute body. Common pattern used by question operator, if/else, switch.Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty at both nextCaseLabel and after GOTO
Control Flow: 1. Evaluate condition → result in variable, stack empty 2. Branch to nextCaseLabel if false → stack empty at label 3. Execute body → result in variable, stack empty 4. Copy result to overall result → stack empty 5. Jump to endLabel → stack empty at label 6. Place nextCaseLabel → stack empty (from branch in step 2)
- Parameters:
conditionCase- IR details for this casenextCaseLabel- Label to jump if condition is falseendLabel- Label to jump after body executionresultVar- Overall result variable (null if no result)
-
processConditionCaseWithoutLabelPlacement
protected void processConditionCaseWithoutLabelPlacement(ConditionCaseDetails conditionCase, org.objectweb.asm.Label nextCaseLabel, org.objectweb.asm.Label endLabel, String resultVar) Process a condition case WITHOUT placing the nextCaseLabel. Use this when the caller needs to control label placement (e.g., if/else-if chains).Stack Frame Invariant: Pre-condition: stack is empty Post-condition: stack is empty after GOTO
- Parameters:
conditionCase- IR details for this casenextCaseLabel- Label to jump if condition is falseendLabel- Label to jump after body executionresultVar- Overall result variable (null if no result)
-