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 }