1 /*
  2  * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package jdk.incubator.code.bytecode.impl;
 26 
 27 import java.lang.classfile.CodeBuilder;
 28 import java.lang.classfile.CodeElement;
 29 import java.lang.classfile.CodeTransform;
 30 import java.lang.classfile.Label;
 31 import java.lang.classfile.Opcode;
 32 import java.lang.classfile.PseudoInstruction;
 33 import java.lang.classfile.instruction.BranchInstruction;
 34 import java.lang.classfile.instruction.ExceptionCatch;
 35 import java.lang.classfile.instruction.LabelTarget;
 36 import java.util.LinkedList;
 37 import java.util.HashSet;
 38 import java.util.List;
 39 import java.util.Set;
 40 
 41 /**
 42  * BranchCompactor is a CodeTransform skipping redundant branches to immediate targets.
 43  */
 44 public final class BranchCompactor implements CodeTransform {
 45 
 46     private BranchInstruction branch;
 47     private final List<CodeElement> buffer = new LinkedList<>();
 48     private final Set<Label> barriers = new HashSet<>();
 49 
 50     public BranchCompactor() {
 51     }
 52 
 53     @Override
 54     public void accept(CodeBuilder cob, CodeElement coe) {
 55         if (coe instanceof ExceptionCatch ec) {
 56             //exception labels are barriers
 57             barriers.add(ec.tryStart());
 58             barriers.add(ec.tryEnd());
 59             barriers.add(ec.handler());
 60         }
 61         if (branch == null) {
 62             if (coe instanceof BranchInstruction bi && isUnconditionalBranch(bi.opcode())) {
 63                 //unconditional branch is stored
 64                 branch = bi;
 65             } else {
 66                 //all other elements are passed
 67                 cob.with(coe);
 68             }
 69         } else {
 70             switch (coe) {
 71                 case LabelTarget lt when barriers.contains(lt.label()) -> {
 72                     //flush the buffer
 73                     atEnd(cob);
 74                     //pass the target
 75                     cob.with(lt);
 76                 }
 77                 case BranchInstruction bi when isUnconditionalBranch(bi.opcode()) -> {
 78                     //buffer branches until the complete run can be compacted
 79                     buffer.add(bi);
 80                 }
 81                 case PseudoInstruction pi -> {
 82                     //buffer pseudo instructions
 83                     buffer.add(pi);
 84                 }
 85                 default -> {
 86                     //any other instruction flushes the branch and buffer
 87                     atEnd(cob);
 88                     //replay the code element
 89                     accept(cob, coe);
 90                 }
 91             }
 92         }
 93     }
 94 
 95     @Override
 96     public void atEnd(CodeBuilder cob) {
 97         Set<Label> targets = new HashSet<>();
 98         for (var it = buffer.listIterator(buffer.size()); it.hasPrevious();) {
 99             switch (it.previous()) {
100                 case LabelTarget lt -> targets.add(lt.label());
101                 case BranchInstruction bi -> {
102                     if (targets.contains(bi.target())) {
103                         it.remove();
104                     } else {
105                         targets.clear();
106                     }
107                 }
108                 default -> {}
109             }
110         }
111         if (branch != null) {
112             //flush the branch unless its target is now immediate
113             if (!targets.contains(branch.target())) cob.with(branch);
114             branch = null;
115         }
116         //flush the buffer
117         buffer.forEach(cob::with);
118         buffer.clear();
119     }
120 
121     static boolean isUnconditionalBranch(Opcode opcode) {
122         return switch (opcode) {
123             case GOTO, ATHROW, GOTO_W, LOOKUPSWITCH, TABLESWITCH -> true;
124             default -> opcode.kind() == Opcode.Kind.RETURN;
125         };
126     }
127 }