1 /*
  2  * Copyright (c) 2024, 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 
 26 package jdk.incubator.code;
 27 
 28 import java.util.List;
 29 import java.util.function.BiFunction;
 30 
 31 /**
 32  * An operation transformer.
 33  */
 34 @FunctionalInterface
 35 // @@@ Change to OpTransform or CodeTransform
 36 public interface OpTransformer {
 37 
 38     /**
 39      * A copying transformer that applies the operation to the block builder, and returning the block builder.
 40      */
 41     OpTransformer COPYING_TRANSFORMER = (block, op) -> {
 42         block.op(op);
 43         return block;
 44     };
 45 
 46     /**
 47      * A transformer that drops location information from operations.
 48      */
 49     OpTransformer DROP_LOCATION_TRANSFORMER = (block, op) -> {
 50         Op.Result r = block.op(op);
 51         r.op().setLocation(Location.NO_LOCATION);
 52         return block;
 53     };
 54 
 55     /**
 56      * A transformer that lowers operations that are {@link Op.Lowerable lowerable},
 57      * and copies other operations.
 58      */
 59     OpTransformer LOWERING_TRANSFORMER = (block, op) -> {
 60         if (op instanceof Op.Lowerable lop) {
 61             return lop.lower(block, null);
 62         } else {
 63             block.op(op);
 64             return block;
 65         }
 66     };
 67 
 68     /**
 69      * Transforms a body starting from a block builder.
 70      *
 71      * @implSpec
 72      * The default implementation {@link #acceptBlock(Block.Builder, Block) accepts} a block builder
 73      * and a block for each block of the body, in order, using this operation transformer.
 74      * The following sequence of actions is performed:
 75      * <ol>
 76      * <li>
 77      * the body's entry block is mapped to the block builder, and the (input) block parameters of the
 78      * body's entry block are mapped to the (output) values, using the builder's context.
 79      * <li>for each (input) block in the body (except the entry block) an (output) block builder is created
 80      * from the builder with the same parameter types as the (input) block, in order.
 81      * The (input) block is mapped to the (output) builder, and the (input) block parameters are mapped to the
 82      * (output) block parameters, using the builder's context.
 83      * <li>
 84      * for each (input) block in the body (in order) the (input) block is transformed
 85      * by {@link #acceptBlock(Block.Builder, Block) accepting} the mapped (output) builder and
 86      * (input) block, using this operation transformer.
 87      * </ol>
 88      *
 89      * @param builder the block builder
 90      * @param body the body to transform
 91      * @param values the values to map to the body's entry block parameters
 92      */
 93     default void acceptBody(Block.Builder builder, Body body, List<? extends Value> values) {
 94         CopyContext cc = builder.context();
 95 
 96         // Map blocks up front, for forward referencing successors
 97         for (Block block : body.blocks()) {
 98             if (block.isEntryBlock()) {
 99                 cc.mapBlock(block, builder);
100                 cc.mapValues(block.parameters(), values);
101             } else {
102                 Block.Builder blockBuilder = builder.block(block.parameterTypes());
103                 cc.mapBlock(block, blockBuilder);
104                 cc.mapValues(block.parameters(), blockBuilder.parameters());
105             }
106         }
107 
108         // Transform blocks
109         for (Block b : body.blocks()) {
110             acceptBlock(cc.getBlock(b), b);
111         }
112     }
113 
114     /**
115      * Transforms a block starting from a block builder.
116      *
117      * @implSpec
118      * The default implementation {@link #acceptOp(Block.Builder, Op) accepts} a block builder
119      * and an operation for each operation of the block, in order, using this operation transformer.
120      * On first iteration the block builder that is applied is block builder passed as an argument
121      * to this method.
122      * On second and subsequent iterations the block builder that is applied is the resulting
123      * block builder of the prior iteration.
124      *
125      * @param builder the block builder
126      * @param block   the block to transform
127      */
128     default void acceptBlock(Block.Builder builder, Block block) {
129         for (Op op : block.ops()) {
130             builder = acceptOp(builder, op);
131         }
132     }
133 
134     /**
135      * Transforms an operation to zero or more operations, appending those operations to a
136      * block builder. Returns a block builder to be used for transforming further operations, such
137      * as subsequent operations from the same block as the given operation.
138      *
139      * @param block the block builder.
140      * @param op    the operation to transform.
141      * @return      the block builder to append to for subsequent operations.
142      */
143     Block.Builder acceptOp(Block.Builder block, Op op);
144 
145     /**
146      * Returns a composed code transform that transforms an operation that first applies
147      * a block builder and operation to the function {@code f}, and then applies
148      * the resulting block builder and the same operation to {@link OpTransformer#acceptOp acceptOp}
149      * of the code transformer {@code after}.
150      * <p>
151      * If the code transformer {@code after} is {@code null} then it is as if a code transformer
152      * is applied that does nothing except return the block builder it was given.
153      *
154      * @param after the code transformer to apply after
155      * @param f the operation transformer function to apply before
156      * @return the composed code transformer
157      */
158     static OpTransformer compose(OpTransformer after, BiFunction<Block.Builder, Op, Block.Builder> f) {
159         return after == null
160                 ? f::apply
161                 : (block, op) -> after.acceptOp(f.apply(block, op), op);
162     }
163 
164     /**
165      * Returns a composed code transformer that first applies a block builder and operation to
166      * {@link OpTransformer#acceptOp acceptOp} of the code transformer {@code before},
167      * and then applies resulting block builder and the same operation to the function {@code f}.
168      * <p>
169      * If the code transformer {@code before} is {@code null} then it is as if a code transformer
170      * is applied that does nothing except return the block builder it was given.
171      *
172      * @param before the code transformer to apply before
173      * @param f the operation transformer function to apply after
174      * @return the composed code transformer
175      */
176     static OpTransformer andThen(OpTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) {
177         return before == null
178                 ? f::apply
179                 : (block, op) -> f.apply(before.acceptOp(block, op), op);
180     }
181 }