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 import java.util.function.Function;
31
32 /**
33 * A code transformer.
34 */
35 @FunctionalInterface
36 public interface CodeTransformer {
37
38 /**
39 * A simplified transformer for only transforming operations.
40 */
41 @FunctionalInterface
42 interface OpTransformer {
43 /**
44 * Transforms an operation to zero or more operations.
45 *
46 * @param op the operation to transform
47 * @param operands the operands of the operation mapped to
48 * values in the transformed code model. If
49 * there is no mapping of an operand then it is
50 * mapped to {@code null}.
51 * @param builder the function to apply zero or more operations
52 * into the transformed code model
53 */
54 void acceptOp(Function<Op, Op.Result> builder, Op op, List<Value> operands);
55 }
56
57 /**
58 * Creates a code transformer that transforms operations using the
59 * given operation transformer.
60 *
61 * @param opTransformer the operation transformer.
62 * @return the code transformer that transforms operations.
63 */
64 static CodeTransformer opTransformer(OpTransformer opTransformer) {
65 final class CodeTransformerOfOps implements CodeTransformer, Function<Op, Op.Result> {
66 Block.Builder builder;
67 Op op;
68
69 @Override
70 public Block.Builder acceptOp(Block.Builder builder, Op op) {
71 this.builder = builder;
72 this.op = op;
73 // Use null if there is no mapping in the output
74 // This can happen if an operation is removed by not applying
75 // it to the builder
76 List<Value> operands = op.operands().stream()
77 .map(v -> builder.context().getValueOrDefault(v, null)).toList();
78 opTransformer.acceptOp(this, op, operands);
79 return builder;
80 }
81
82 @Override
83 public Op.Result apply(Op op) {
84 Op.Result result = builder.op(op);
85 builder.context().mapValue(this.op.result(), result);
86 return result;
87 }
88 }
89 return new CodeTransformerOfOps();
90 }
91
92 /**
93 * A copying transformer that applies the operation to the block builder, and returning the block builder.
94 */
95 CodeTransformer COPYING_TRANSFORMER = (block, op) -> {
96 block.op(op);
97 return block;
98 };
99
100 /**
101 * A transformer that drops location information from operations.
102 */
103 CodeTransformer DROP_LOCATION_TRANSFORMER = (block, op) -> {
104 Op.Result r = block.op(op);
105 r.op().setLocation(Location.NO_LOCATION);
106 return block;
107 };
108
109 /**
110 * A transformer that lowers operations that are {@link Op.Lowerable lowerable},
111 * and copies other operations.
112 */
113 CodeTransformer LOWERING_TRANSFORMER = (block, op) -> {
114 if (op instanceof Op.Lowerable lop) {
115 return lop.lower(block, null);
116 } else {
117 block.op(op);
118 return block;
119 }
120 };
121
122 /**
123 * Transforms a body starting from a block builder.
124 *
125 * @implSpec
126 * The default implementation {@link #acceptBlock(Block.Builder, Block) accepts} a block builder
127 * and a block for each block of the body, in order, using this operation transformer.
128 * The following sequence of actions is performed:
129 * <ol>
130 * <li>
131 * the body's entry block is mapped to the block builder, and the (input) block parameters of the
132 * body's entry block are mapped to the (output) values, using the builder's context.
133 * <li>for each (input) block in the body (except the entry block) an (output) block builder is created
134 * from the builder with the same parameter types as the (input) block, in order.
135 * The (input) block is mapped to the (output) builder, and the (input) block parameters are mapped to the
136 * (output) block parameters, using the builder's context.
137 * <li>
138 * for each (input) block in the body (in order) the (input) block is transformed
139 * by {@link #acceptBlock(Block.Builder, Block) accepting} the mapped (output) builder and
140 * (input) block, using this operation transformer.
141 * </ol>
142 *
143 * @param builder the block builder
144 * @param body the body to transform
145 * @param values the values to map to the body's entry block parameters
146 */
147 default void acceptBody(Block.Builder builder, Body body, List<? extends Value> values) {
148 CodeContext cc = builder.context();
149
150 // Map blocks up front, for forward referencing successors
151 for (Block block : body.blocks()) {
152 if (block.isEntryBlock()) {
153 cc.mapBlock(block, builder);
154 cc.mapValues(block.parameters(), values);
155 } else {
156 Block.Builder blockBuilder = builder.block(block.parameterTypes());
157 cc.mapBlock(block, blockBuilder);
158 cc.mapValues(block.parameters(), blockBuilder.parameters());
159 }
160 }
161
162 // Transform blocks
163 for (Block b : body.blocks()) {
164 acceptBlock(cc.getBlock(b), b);
165 }
166 }
167
168 /**
169 * Transforms a block starting from a block builder.
170 *
171 * @implSpec
172 * The default implementation {@link #acceptOp(Block.Builder, Op) accepts} a block builder
173 * and an operation for each operation of the block, in order, using this operation transformer.
174 * On first iteration the block builder that is applied is block builder passed as an argument
175 * to this method.
176 * On second and subsequent iterations the block builder that is applied is the resulting
177 * block builder of the prior iteration.
178 *
179 * @param builder the block builder
180 * @param block the block to transform
181 */
182 default void acceptBlock(Block.Builder builder, Block block) {
183 for (Op op : block.ops()) {
184 builder = acceptOp(builder, op);
185 }
186 }
187
188 /**
189 * Transforms an operation to zero or more operations, appending those operations to a
190 * block builder. Returns a block builder to be used for transforming further operations, such
191 * as subsequent operations from the same block as the given operation.
192 *
193 * @param block the block builder.
194 * @param op the operation to transform.
195 * @return the block builder to append to for subsequent operations.
196 */
197 Block.Builder acceptOp(Block.Builder block, Op op);
198
199 /**
200 * Returns a composed code transform that transforms an operation that first applies
201 * a block builder and operation to the function {@code f}, and then applies
202 * the resulting block builder and the same operation to {@link CodeTransformer#acceptOp acceptOp}
203 * of the code transformer {@code after}.
204 * <p>
205 * If the code transformer {@code after} is {@code null} then it is as if a code transformer
206 * is applied that does nothing except return the block builder it was given.
207 *
208 * @param after the code transformer to apply after
209 * @param f the operation transformer function to apply before
210 * @return the composed code transformer
211 */
212 static CodeTransformer compose(CodeTransformer after, BiFunction<Block.Builder, Op, Block.Builder> f) {
213 return after == null
214 ? f::apply
215 : (block, op) -> after.acceptOp(f.apply(block, op), op);
216 }
217
218 /**
219 * Returns a composed code transformer that first applies a block builder and operation to
220 * {@link CodeTransformer#acceptOp acceptOp} of the code transformer {@code before},
221 * and then applies resulting block builder and the same operation to the function {@code f}.
222 * <p>
223 * If the code transformer {@code before} is {@code null} then it is as if a code transformer
224 * is applied that does nothing except return the block builder it was given.
225 *
226 * @param before the code transformer to apply before
227 * @param f the operation transformer function to apply after
228 * @return the composed code transformer
229 */
230 static CodeTransformer andThen(CodeTransformer before, BiFunction<Block.Builder, Op, Block.Builder> f) {
231 return before == null
232 ? f::apply
233 : (block, op) -> f.apply(before.acceptOp(block, op), op);
234 }
235 }