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 }