public static void duplicateMergedCatchBlocks(@NotNull ControlFlowGraph graph, @NotNull StructClass cl) {
    if (!cl.hasRecordPatternSupport()) {
      return;
    }
    Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();
    for (ExceptionRangeCFG range : graph.getExceptions()) {
      mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range);
    }

    for (Entry<BasicBlock, Set<ExceptionRangeCFG>> ent : mapRanges.entrySet()) {
      BasicBlock handler = ent.getKey();
      Set<ExceptionRangeCFG> ranges = ent.getValue();

      if (ranges.size() == 1) {
        continue;
      }
      if (handler.getLastInstruction().opcode != CodeConstants.opc_athrow) {
        continue;
      }
      Set<String> exceptions = ranges.stream()
        .map(t -> t.getExceptionTypes())
        .flatMap(t -> t != null ? t.stream() : Stream.empty())
        .collect(Collectors.toSet());
      List<BasicBlock> successors = handler.getSuccessors();
      if (successors != null && successors.size() == 1 && successors.get(0).getSuccessors().isEmpty() &&
          successors.get(0).getSuccessorExceptions().isEmpty() &&
          //exceptions contain only one type of exceptions, and it is not null, because null defines `finally` blocks
          exceptions.size() == 1 && !exceptions.contains(null)) {
        for (ExceptionRangeCFG range : ranges) {
          BasicBlock newHandler = handler.clone(++graph.last_id);
          graph.getBlocks().addWithKey(newHandler, newHandler.id);
          // only exception predecessors from this range considered
          List<BasicBlock> lstPredExceptions = new ArrayList<>(handler.getPredecessorExceptions());
          lstPredExceptions.retainAll(range.getProtectedRange());
          // replace predecessors
          for (BasicBlock pred : lstPredExceptions) {
            ExceptionRangeCFG previousEdge = graph.getExceptionRange(handler, pred);
            pred.replaceSuccessor(handler, newHandler);
            if (previousEdge != null) {
              previousEdge.setHandler(newHandler);
            }
          }
          // replace successors
          List<BasicBlock> scExceptions = new ArrayList<>(handler.getSuccessorExceptions());
          for (BasicBlock nextException : scExceptions) {
            ExceptionRangeCFG nextEdge = graph.getExceptionRange(nextException, handler);
            newHandler.addSuccessorException(nextException);
            nextEdge.getProtectedRange().add(newHandler);
          }
          //add fast exit
          newHandler.addSuccessor(successors.get(0));
          for (BasicBlock successorException : handler.getSuccessorExceptions()) {
            newHandler.addSuccessorException(successorException);
            ExceptionRangeCFG previousEdge = graph.getExceptionRange(successorException, handler);
            if (previousEdge != null) {
              ArrayList<BasicBlock> newRanges = new ArrayList<>();
              newRanges.add(newHandler);
              ExceptionRangeCFG subRange = new ExceptionRangeCFG(newRanges, successorException, previousEdge.getExceptionTypes());
              graph.getExceptions().add(subRange);
              successorException.addPredecessorException(newHandler);
            }
          }
          range.setHandler(newHandler);
        }

        for (BasicBlock successorException : handler.getSuccessorExceptions()) {
          successorException.removePredecessorException(handler);
          if (successorException.getPredecessorExceptions().isEmpty()) {
            graph.removeBlock(successorException);
          }
        }
        graph.removeBlock(handler);
      }
    }
  }
public static void duplicateMergedCatchBlocks(@NotNull ControlFlowGraph graph, @NotNull StructClass cl)