1 /*
  2  * Copyright (c) 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 import jdk.incubator.code.Block;
 25 import jdk.incubator.code.CodeTransformer;
 26 import jdk.incubator.code.Op;
 27 import jdk.incubator.code.dialect.core.CoreOp;
 28 import jdk.incubator.code.dialect.core.CoreOp.*;
 29 import jdk.incubator.code.dialect.java.JavaOp.*;
 30 
 31 /// Normalizes lifted fragments of the same exception region
 32 ///
 33 /// ```
 34 ///           entry
 35 ///             |
 36 ///     enter R#1 handler H
 37 ///        /          \                         entry
 38 /// protected A    protected B                    |
 39 ///      |              |                 enter R handler H
 40 ///   exit R#1       exit R#1                /         \
 41 ///      |              |         ->  protected A   protected B
 42 /// branch adapter branch adapter             \         /
 43 ///        \          /                       protected C
 44 ///     enter R#2 handler H                       |
 45 ///             |                               exit R
 46 ///        protected C
 47 ///             |
 48 ///          exit R#2
 49 /// ```
 50 final class NormalizeExceptionRegionsTransformer implements CodeTransformer {
 51 
 52     private boolean modified;
 53 
 54     static FuncOp transform(FuncOp func) {
 55         var t = new NormalizeExceptionRegionsTransformer();
 56         do {
 57             // Inner regions are exposed after the outer are collapsed
 58             t.modified = false;
 59             func = func.transform(t);
 60         } while (t.modified);
 61         return func;
 62     }
 63 
 64     @Override
 65     public void acceptBlock(Block.Builder builder, Block b) {
 66         // Skip blocks whose terminating op was mapped by a previous collapse
 67         if (!builder.context().queryValue(b.terminatingOp().result()).isPresent()) {
 68             CodeTransformer.super.acceptBlock(builder, b);
 69         }
 70     }
 71 
 72     @Override
 73     public Block.Builder acceptOp(Block.Builder builder, Op op) {
 74         // Collapse matching exit/re-enter boundaries
 75         if (!(op instanceof ExceptionRegionExit exit) || !collapseExitToReenter(builder, exit)) {
 76             builder.add(op);
 77         }
 78         return builder;
 79     }
 80 
 81     private boolean collapseExitToReenter(Block.Builder builder, ExceptionRegionExit exit) {
 82         // Match exit -> optional branch adapter -> re-enter
 83         ExceptionRegionEnter exitedEnter = exit.enterOp();
 84         Block exitTarget = exit.endReference().targetBlock();
 85         BranchOp adapterBranch = asBranchAdapter(exitTarget);
 86         Block enterBlock = adapterBranch == null ? exitTarget : adapterBranch.branch().targetBlock();
 87         if (enterBlock.ops().size() != 1
 88                 || !(enterBlock.terminatingOp() instanceof ExceptionRegionEnter reenter)
 89                 || !isExitTargetOf(exitTarget, exitedEnter)
 90                 || (adapterBranch != null && !isOnlyReachedByExitAdapters(enterBlock, exitedEnter))
 91                 || !sameCatchTargets(exitedEnter, reenter)
 92                 || !reenter.result().isDominatedBy(exitedEnter.result())) {
 93             return false;
 94         }
 95         // Reuse the old enter result and skip the adapter blocks
 96         var cc = builder.context();
 97         cc.mapValue(reenter.result(), cc.getValue(exitedEnter.result()));
 98         cc.mapValues(exitTarget.parameters(), cc.getValues(exit.endReference().arguments()));
 99         if (adapterBranch != null) {
100             cc.mapValues(enterBlock.parameters(), cc.getValues(adapterBranch.branch().arguments()));
101         }
102         Op.Result result = builder.add(CoreOp.branch(cc.getReferenceOrCreate(reenter.startReference())));
103         if (adapterBranch != null) {
104             cc.mapValue(adapterBranch.result(), result);
105         }
106         modified = true;
107         return true;
108     }
109 
110     // Return a branch adapter, if this block is one
111     private static BranchOp asBranchAdapter(Block block) {
112         return block.ops().size() == 1 && block.terminatingOp() instanceof BranchOp branch
113                 ? branch
114                 : null;
115     }
116 
117     // All predecessors must exit the same enter
118     private static boolean isExitTargetOf(Block block, ExceptionRegionEnter enter) {
119         return !block.predecessors().isEmpty()
120                 && block.predecessors().stream().allMatch(predecessor ->
121                         predecessor.terminatingOp() instanceof ExceptionRegionExit e && e.enterOp() == enter);
122     }
123 
124     // Several branch adapters may feed the same re-enter block
125     private static boolean isOnlyReachedByExitAdapters(Block block, ExceptionRegionEnter enter) {
126         return block.predecessors().stream().allMatch(predecessor ->
127                 predecessor.terminatingOp() instanceof BranchOp branch && isExitTargetOf(predecessor, enter));
128     }
129 
130     // Compare real handler targets
131     private static boolean sameCatchTargets(ExceptionRegionEnter first, ExceptionRegionEnter second) {
132         return first.catchReferences().stream().map(NormalizeExceptionRegionsTransformer::skipBranchAdapter).toList()
133                 .equals(second.catchReferences().stream().map(NormalizeExceptionRegionsTransformer::skipBranchAdapter).toList());
134     }
135 
136     // Skip one branch adapter
137     private static Block skipBranchAdapter(Block.Reference reference) {
138         Block target = reference.targetBlock();
139         BranchOp adapterBranch = asBranchAdapter(target);
140         return adapterBranch != null
141                 ? adapterBranch.branch().targetBlock()
142                 : target;
143     }
144 }