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.ArrayList;
 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<PseudoInstruction> buffer = new ArrayList<>();
 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 LabelTarget lt when branch.target() == lt.label() -> {
 78                     //skip branch to immediate target
 79                     branch = null;
 80                     //flush the buffer
 81                     atEnd(cob);
 82                     //pass the target
 83                     cob.with(lt);
 84                 }
 85                 case PseudoInstruction pi -> {
 86                     //buffer pseudo instructions
 87                     buffer.add(pi);
 88                 }
 89                 default -> {
 90                     //any other instruction flushes the branch and buffer
 91                     atEnd(cob);
 92                     //replay the code element
 93                     accept(cob, coe);
 94                 }
 95             }
 96         }
 97     }
 98 
 99     @Override
100     public void atEnd(CodeBuilder cob) {
101         if (branch != null) {
102             //flush the branch
103             cob.with(branch);
104             branch = null;
105         }
106         //flush the buffer
107         buffer.forEach(cob::with);
108         buffer.clear();
109     }
110 
111     static boolean isUnconditionalBranch(Opcode opcode) {
112         return switch (opcode) {
113             case GOTO, ATHROW, GOTO_W, LOOKUPSWITCH, TABLESWITCH -> true;
114             default -> opcode.kind() == Opcode.Kind.RETURN;
115         };
116     }
117 }