private void computeMaxStackAndLocal()

in src/main/java/com/amazon/corretto/hotpatch/org/objectweb/asm/MethodWriter.java [1660:1764]


  private void computeMaxStackAndLocal() {
    // Complete the control flow graph with exception handler blocks.
    Handler handler = firstHandler;
    while (handler != null) {
      Label handlerBlock = handler.handlerPc;
      Label handlerRangeBlock = handler.startPc;
      Label handlerRangeEnd = handler.endPc;
      // Add handlerBlock as a successor of all the basic blocks in the exception handler range.
      while (handlerRangeBlock != handlerRangeEnd) {
        if ((handlerRangeBlock.flags & Label.FLAG_SUBROUTINE_CALLER) == 0) {
          handlerRangeBlock.outgoingEdges =
              new Edge(Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges);
        } else {
          // If handlerRangeBlock is a JSR block, add handlerBlock after the first two outgoing
          // edges to preserve the hypothesis about JSR block successors order (see
          // {@link #visitJumpInsn}).
          handlerRangeBlock.outgoingEdges.nextEdge.nextEdge =
              new Edge(
                  Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges.nextEdge.nextEdge);
        }
        handlerRangeBlock = handlerRangeBlock.nextBasicBlock;
      }
      handler = handler.nextHandler;
    }

    // Complete the control flow graph with the successor blocks of subroutines, if needed.
    if (hasSubroutines) {
      // First step: find the subroutines. This step determines, for each basic block, to which
      // subroutine(s) it belongs. Start with the main "subroutine":
      short numSubroutines = 1;
      firstBasicBlock.markSubroutine(numSubroutines);
      // Then, mark the subroutines called by the main subroutine, then the subroutines called by
      // those called by the main subroutine, etc.
      for (short currentSubroutine = 1; currentSubroutine <= numSubroutines; ++currentSubroutine) {
        Label basicBlock = firstBasicBlock;
        while (basicBlock != null) {
          if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0
              && basicBlock.subroutineId == currentSubroutine) {
            Label jsrTarget = basicBlock.outgoingEdges.nextEdge.successor;
            if (jsrTarget.subroutineId == 0) {
              // If this subroutine has not been marked yet, find its basic blocks.
              jsrTarget.markSubroutine(++numSubroutines);
            }
          }
          basicBlock = basicBlock.nextBasicBlock;
        }
      }
      // Second step: find the successors in the control flow graph of each subroutine basic block
      // 'r' ending with a RET instruction. These successors are the virtual successors of the basic
      // blocks ending with JSR instructions (see {@link #visitJumpInsn)} that can reach 'r'.
      Label basicBlock = firstBasicBlock;
      while (basicBlock != null) {
        if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) {
          // By construction, jsr targets are stored in the second outgoing edge of basic blocks
          // that ends with a jsr instruction (see {@link #FLAG_SUBROUTINE_CALLER}).
          Label subroutine = basicBlock.outgoingEdges.nextEdge.successor;
          subroutine.addSubroutineRetSuccessors(basicBlock);
        }
        basicBlock = basicBlock.nextBasicBlock;
      }
    }

    // Data flow algorithm: put the first basic block in a list of blocks to process (i.e. blocks
    // whose input stack size has changed) and, while there are blocks to process, remove one
    // from the list, update the input stack size of its successor blocks in the control flow
    // graph, and add these blocks to the list of blocks to process (if not already done).
    Label listOfBlocksToProcess = firstBasicBlock;
    listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST;
    int maxStackSize = maxStack;
    while (listOfBlocksToProcess != Label.EMPTY_LIST) {
      // Remove a basic block from the list of blocks to process. Note that we don't reset
      // basicBlock.nextListElement to null on purpose, to make sure we don't reprocess already
      // processed basic blocks.
      Label basicBlock = listOfBlocksToProcess;
      listOfBlocksToProcess = listOfBlocksToProcess.nextListElement;
      // Compute the (absolute) input stack size and maximum stack size of this block.
      int inputStackTop = basicBlock.inputStackSize;
      int maxBlockStackSize = inputStackTop + basicBlock.outputStackMax;
      // Update the absolute maximum stack size of the method.
      if (maxBlockStackSize > maxStackSize) {
        maxStackSize = maxBlockStackSize;
      }
      // Update the input stack size of the successor blocks of basicBlock in the control flow
      // graph, and add these blocks to the list of blocks to process, if not already done.
      Edge outgoingEdge = basicBlock.outgoingEdges;
      if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) {
        // Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual
        // edges which lead to the instruction just after the jsr, and do not correspond to a
        // possible execution path (see {@link #visitJumpInsn} and
        // {@link Label#FLAG_SUBROUTINE_CALLER}).
        outgoingEdge = outgoingEdge.nextEdge;
      }
      while (outgoingEdge != null) {
        Label successorBlock = outgoingEdge.successor;
        if (successorBlock.nextListElement == null) {
          successorBlock.inputStackSize =
              (short) (outgoingEdge.info == Edge.EXCEPTION ? 1 : inputStackTop + outgoingEdge.info);
          successorBlock.nextListElement = listOfBlocksToProcess;
          listOfBlocksToProcess = successorBlock;
        }
        outgoingEdge = outgoingEdge.nextEdge;
      }
    }
    this.maxStack = maxStackSize;
  }