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.Function;
 30 
 31 /**
 32  * A code transformer.
 33  * <p>
 34  * A code transformer transforms an input code model into an output code model. It traverses the input code model and
 35  * builds the output code model by accepting input bodies, blocks, and operations and emitting output blocks and
 36  * operations using block builders.
 37  * <p>
 38  * During transformation, a block builder may serve as the current output block builder when accepting bodies, blocks,
 39  * and operations. A transformation emits an output operation by appending it using an output block builder.
 40  * A transformation emits an output block by creating a block builder for that block and appending operations using that
 41  * block builder. Emission is commonly performed by implementations of {@link #acceptOp(Block.Builder, Op)}, and less
 42  * commonly so by implementations that override {@link #acceptBlock(Block.Builder, Block)} and
 43  * {@link #acceptBody(Block.Builder, Body, List)}.
 44  * <p>
 45  * By default, traversal transforms an input body by transforming each input block of the body, in order, and transforms
 46  * an input block by transforming each input operation of the block, in order. The single abstract method
 47  * {@link #acceptOp(Block.Builder, Op)} is the primitive transformation step. Implementations of that method may emit
 48  * output blocks and operations. Appending an attached or root operation using an output block builder may recursively
 49  * invoke the code transformer for descendant code elements.
 50  * <p>
 51  * A transformation uses the {@link CodeContext} of an output block builder to record correspondence between input
 52  * code items and outputs. Some mappings are established implicitly: by the default traversal of an input body,
 53  * for mappings between input blocks and output block builders and for mappings between input block parameters and
 54  * output values; and by appending attached or root operations, for mappings between appended operation results and
 55  * output operation results. By default, block reference mappings are never established implicitly.
 56  * <p>
 57  * Transformations that drop, replace, or expand input code elements are responsible for explicitly establishing any
 58  * mappings required by the transformation of subsequent code elements.
 59  * <p>
 60  * Code transformer implementations are not required to be thread-safe. Code transformations operate on block builders
 61  * and code contexts that are not thread-safe.
 62  *
 63  * @see Block.Builder#block(List)
 64  * @see Block.Builder#op(Op)
 65  * @see CodeContext
 66  */
 67 @FunctionalInterface
 68 public interface CodeTransformer {
 69 
 70     /**
 71      * A simplified transformer for transforming one input operation into an output code model.
 72      */
 73     @FunctionalInterface
 74     interface OpTransformer {
 75         /**
 76          * Transforms one input operation into the output code model.
 77          * <p>
 78          * Implementations of this method may emit zero or more output operations into the output model by applying the
 79          * given operation-building function to operations.
 80          * <p>
 81          * Implementations can choose to drop the input operation by not applying the function, copy it by applying the
 82          * function to the input operation, replace it by applying the function to a different output operation, or
 83          * expand it by applying the function to multiple output operations.
 84          * <p>
 85          * Each application of the operation-building function implicitly maps the input operation's result to the
 86          * result returned by the function, which is the result of the emitted output operation. A later application
 87          * replaces the mapping established by any prior application. If the function is not applied, no mapping is
 88          * established for the input operation's result.
 89          * <p>
 90          * The given operands list contains, in order, the output values currently mapped from the input operation's
 91          * operands. It has the same number of values as the input operation's operands, but if an input operand
 92          * has no mapped output value, the corresponding output value is {@code null}.
 93          * <p>
 94          * The operation-building function encapsulates the current output block builder for this transformation step.
 95          * Applying the function {@link Block.Builder#op(Op) appends} an operation using the current output block
 96          * builder, which will perform <a href="Block.Builder.html#transform-on-append"><i>transform-on-append</i></a>
 97          * when appending the input operation, or any other attached or root operation.
 98          *
 99          * @param builder  the operation-building function
100          * @param op       the input operation to transform
101          * @param operands the mapped output values for the input operation's operands
102          */
103         void acceptOp(Function<Op, Op.Result> builder, Op op, List<Value> operands);
104     }
105 
106     /**
107      * Creates a code transformer that transforms operations using the given operation transformer.
108      * <p>
109      * This method is intended for simplified transformations that only emit output operations using the current output
110      * block builder. Transformations that need to emit output blocks, explicitly establish mappings, customize body or
111      * block traversal, or return a different continuation builder should directly implement {@code CodeTransformer}.
112      * <p>
113      * The created code transformer uses the default {@link #acceptBody(Block.Builder, Body, List)} and
114      * {@link #acceptBlock(Block.Builder, Block)} traversal. Its {@link #acceptOp(Block.Builder, Op)} implementation
115      * invokes the given operation transformer with an operation-building function for the current output block builder,
116      * the input operation, and output values currently mapped from the input operation's operands.
117      * <p>
118      * Applying the operation-building function appends the operation using the current output block builder and maps
119      * the input operation's result to the result of the appended operation, as specified by
120      * {@link OpTransformer#acceptOp(Function, Op, List)}.
121      * <p>
122      * After the operation transformer returns, the created code transformer returns the current output block builder as
123      * the continuation builder for the next input operation.
124      *
125      * @param opTransformer the operation transformer.
126      * @return the code transformer that transforms operations.
127      */
128     static CodeTransformer opTransformer(OpTransformer opTransformer) {
129         return (builder, inputOp) -> {
130             // Allocate operation-building function capturing builder and inputOp
131             // This is simpler and safer that using fields holding the builder and inputOp
132             // and protecting use against reentry of calls to builder.op for an attached
133             // operation that is transformed and contains bodies
134             // @@@ If performance is an issue consider changing
135             Function<Op, Op.Result> opBuilder = outputOp -> {
136                 Op.Result result = builder.op(outputOp);
137                 builder.context().mapValue(inputOp.result(), result);
138                 return result;
139             };
140 
141             List<Value> outputOperands = inputOp.operands().stream()
142                     .map(v -> builder.context().queryValue(v).orElse(null)).toList();
143 
144             opTransformer.acceptOp(opBuilder, inputOp, outputOperands);
145             return builder;
146         };
147     }
148 
149     /**
150      * A copying transformer that appends the operation using the block builder, and returns the block builder.
151      */
152     CodeTransformer COPYING_TRANSFORMER = (builder, op) -> {
153         builder.op(op);
154         return builder;
155     };
156 
157     /**
158      * A transformer that drops location information from operations.
159      */
160     CodeTransformer DROP_LOCATION_TRANSFORMER = (builder, op) -> {
161         Op.Result r = builder.op(op);
162         r.op().setLocation(Op.Location.NO_LOCATION);
163         return builder;
164     };
165 
166     /**
167      * A transformer that lowers operations that are {@link Op.Lowerable lowerable},
168      * and copies other operations.
169      */
170     CodeTransformer LOWERING_TRANSFORMER = (builder, op) -> {
171         if (op instanceof Op.Lowerable lop) {
172             return lop.lower(builder, null);
173         } else {
174             builder.op(op);
175             return builder;
176         }
177     };
178 
179     /**
180      * Transforms one input body into the output model.
181      *
182      * @implSpec
183      * The default implementation first establishes mappings, and then transforms each input block of the body, in
184      * order, using this code transformer.
185      * <p>
186      * The default implementation first <i>implicitly</i> establishes block mappings and value mappings for block
187      * parameters using the given block builder's code context, as follows:
188      * <ul>
189      * <li>
190      * The entry block of the input body is mapped to the given block builder, which is the current output block builder
191      * for the entry block. A prefix of the entry block's parameters is mapped, in order, to the given entry values.
192      * Any remaining entry block parameters are unmapped.
193      * <li>
194      * For each non-entry block of the input body, an output block builder is created from the given block builder with
195      * the same sequence of parameter types as the input block. The input block is mapped to that output block builder,
196      * and the input block parameters are mapped exactly to the output block builder's parameters.
197      * </ul>
198      * Then, for each input block, in order, the default implementation invokes
199      * {@link #acceptBlock(Block.Builder, Block)} with the output block builder mapped from the input block and the
200      * input block.
201      *
202      * @param builder the current output block builder for the input body's entry block
203      * @param body the input body to transform
204      * @param entryValues the output entry values to map, in order, from a prefix of the input body's entry block
205      *                    parameters
206      * @throws IllegalArgumentException if there are more output entry values than entry block parameters
207      * @see #acceptBlock(Block.Builder, Block)
208      */
209     default void acceptBody(Block.Builder builder, Body body, List<? extends Value> entryValues) {
210         CodeContext cc = builder.context();
211 
212         // Map blocks up front, for forward referencing successors
213         for (Block block : body.blocks()) {
214             if (block.isEntryBlock()) {
215                 cc.mapBlock(block, builder);
216                 cc.mapValuePrefix(block.parameters(), entryValues);
217             } else {
218                 Block.Builder blockBuilder = builder.block(block.parameterTypes());
219                 cc.mapBlock(block, blockBuilder);
220                 cc.mapValues(block.parameters(), blockBuilder.parameters());
221             }
222         }
223 
224         // Transform blocks
225         for (Block b : body.blocks()) {
226             acceptBlock(cc.getBlock(b), b);
227         }
228     }
229 
230     /**
231      * Transforms one input block into the output model.
232      *
233      * @implSpec
234      * The default implementation transforms each input operation of the given block, in order, using this code
235      * transformer.
236      * <p>
237      * The given block builder is the current output block builder for the first input operation. For each input
238      * operation, the default implementation invokes {@link #acceptOp(Block.Builder, Op)} with the current output block
239      * builder and the input operation. The continuation builder returned by that invocation becomes the current output
240      * block builder for the next input operation from the same input block.
241      *
242      * @param builder the current output block builder
243      * @param block   the input block to transform
244      * @see #acceptOp(Block.Builder, Op)
245      */
246     default void acceptBlock(Block.Builder builder, Block block) {
247         for (Op op : block.ops()) {
248             builder = acceptOp(builder, op);
249         }
250     }
251 
252     /**
253      * Transforms one input operation into the output model.
254      * <p>
255      * Implementations of this method may emit zero or more output operations and output blocks into the output model by
256      * using the given block builder, which is the current output block builder for this transformation, or any other
257      * block builder created for this transformation. Such a block builder can be used to:
258      * <ul>
259      * <li>
260      * emit an output operation, by using the block builder to {@link Block.Builder#op(Op) append} the operation; and
261      * <li>
262      * emit an output block, by using the builder to {@link Block.Builder#block(List) create} a block builder for that
263      * output block and appending operations using the created block builder.
264      * </ul>
265      * <p>
266      * Implementations can choose to drop the input operation by not emitting any output operation, copy it by
267      * appending it, replace it by appending a different output operation, or expand it by appending multiple
268      * output operations and creating additional block builders.
269      * <p>
270      * A block builder will perform <a href="Block.Builder.html#transform-on-append"><i>transform-on-append</i></a> when
271      * appending the input operation, or any other attached or root operation. More specifically:
272      * <ul>
273      * <li>
274      * if the block builder's code transformer is the same as this code transformer, as is the case for the current
275      * output block builder, this code transformer may be recursively invoked for any descendant input operations.
276      * <li>
277      * if the appended operation has a result, the block builder's code context is used to <i>implicitly</i> establish
278      * a mapping between that result and the result of the emitted output operation, if no such mapping already exists.
279      * </ul>
280      * <p>
281      * If an implementation drops, replaces, or expands the input operation, or creates output blocks that correspond to
282      * input blocks, it is responsible for <i>explicitly</i> establishing mappings between input and output values,
283      * blocks and block references, that are required for the transformation of subsequent input bodies, blocks, and
284      * operations. Mappings are established using a block builder's code context for this transformation. For example,
285      * replacing an input operation whose result is used by a subsequent input operation requires that a mapping be
286      * explicitly established between the input and output operation results; otherwise an exception may be thrown if
287      * transformation later appends the subsequent operation.
288      * <p>
289      * An implementation returns the continuation builder to use, as the current output block builder, for subsequent
290      * input operations from the same input block. The returned builder may be the current output block builder, or
291      * another builder created for this transformation.
292      *
293      * @apiNote
294      * A code transformer that copies each input operation, and as a result copies the input code model, can be
295      * implemented as follows:
296      * {@snippet lang = "java":
297      * CodeTransformer copyingTransformer = (builder, inputOp) -> {
298      *     builder.op(inputOp);
299      *     return builder;
300      * };
301      * }
302      * The call to {@code builder.op(inputOp)} performs transform-on-append. If the input operation has descendant code
303      * elements, this code transformer is recursively invoked to transform those elements. The result is that all input
304      * code elements are copied into the output model.
305      * <p>
306      * For convenience {@code CodeTransformer} provides such an implementation,
307      * {@link CodeTransformer#COPYING_TRANSFORMER}.
308      *
309      * @param builder the current output block builder.
310      * @param op      the input operation to transform.
311      * @return the continuation builder to use to transform subsequent input operations from the same input block
312      * @see Block.Builder#op(Op)
313      * @see CodeTransformer#COPYING_TRANSFORMER
314      */
315     Block.Builder acceptOp(Block.Builder builder, Op op);
316 }