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 }