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.Objects;
 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 performs no action on the block builder.
 48      */
 49     OpTransformer NOOP_TRANSFORMER = (block, op) -> block;
 50 
 51     /**
 52      * A transformer that drops location information from operations.
 53      */
 54     OpTransformer DROP_LOCATION_TRANSFORMER = (block, op) -> {
 55         Op.Result r = block.op(op);
 56         r.op().setLocation(Location.NO_LOCATION);
 57         return block;
 58     };
 59 
 60     /**
 61      * A transformer that lowers operations that are {@link Op.Lowerable lowerable},
 62      * and copies other operations.
 63      */
 64     OpTransformer LOWERING_TRANSFORMER = (block, op) -> {
 65         if (op instanceof Op.Lowerable lop) {
 66             return lop.lower(block);
 67         } else {
 68             block.op(op);
 69             return block;
 70         }
 71     };
 72 
 73     /**
 74      * Transforms a body starting from a block builder.
 75      *
 76      * @implSpec
 77      * The default implementation {@link #acceptBlock(Block.Builder, Block) accepts} a block builder
 78      * and a block for each block of the body, in order, using this operation transformer.
 79      * The following sequence of actions is performed:
 80      * <ol>
 81      * <li>
 82      * the body's entry block is mapped to the block builder, and the (input) block parameters of the
 83      * body's entry block are mapped to the (output) values, using the builder's context.
 84      * <li>for each (input) block in the body (except the entry block) an (output) block builder is created
 85      * from the builder with the same parameter types as the (input) block, in order.
 86      * The (input) block is mapped to the (output) builder, and the (input) block parameters are mapped to the
 87      * (output) block parameters, using the builder's context.
 88      * <li>
 89      * for each (input) block in the body (in order) the (input) block is transformed
 90      * by {@link #acceptBlock(Block.Builder, Block) accepting} the mapped (output) builder and
 91      * (input) block, using this operation transformer.
 92      * </ol>
 93      *
 94      * @param builder the block builder
 95      * @param body the body to transform
 96      * @param values the values to map to the body's entry block parameters
 97      */
 98     default void acceptBody(Block.Builder builder, Body body, List<? extends Value> values) {
 99         CopyContext cc = builder.context();
100 
101         // Map blocks up front, for forward referencing successors
102         for (Block block : body.blocks()) {
103             if (block.isEntryBlock()) {
104                 cc.mapBlock(block, builder);
105                 cc.mapValues(block.parameters(), values);
106             } else {
107                 Block.Builder blockBuilder = builder.block(block.parameterTypes());
108                 cc.mapBlock(block, blockBuilder);
109                 cc.mapValues(block.parameters(), blockBuilder.parameters());
110             }
111         }
112 
113         // Transform blocks
114         for (Block b : body.blocks()) {
115             acceptBlock(cc.getBlock(b), b);
116         }
117     }
118 
119     /**
120      * Transforms a block starting from a block builder.
121      *
122      * @implSpec
123      * The default implementation {@link #acceptOp(Block.Builder, Op) accepts} a block builder
124      * and an operation for each operation of the block, in order, using this operation transformer.
125      * On first iteration the block builder that is applied is block builder passed as an argument
126      * to this method.
127      * On second and subsequent iterations the block builder that is applied is the resulting
128      * block builder of the prior iteration.
129      *
130      * @param builder the block builder
131      * @param block   the block to transform
132      * @throws NullPointerException if a resulting block builder is null
133      */
134     default void acceptBlock(Block.Builder builder, Block block) {
135         for (Op op : block.ops()) {
136             builder = acceptOp(builder, op);
137             // @@@ See andThen composition
138             Objects.requireNonNull(builder);
139         }
140     }
141 
142     /**
143      * Transforms an operation to zero or more operations, appending those operations to a
144      * block builder. Returns a block builder to be used for transforming further operations, such
145      * as subsequent operations from the same block as the given operation.
146      *
147      * @param block the block builder.
148      * @param op    the operation to transform.
149      * @return      the block builder to append to for subsequent operations.
150      */
151     Block.Builder acceptOp(Block.Builder block, Op op);
152 
153     default OpTransformer compose(OpTransformer before) {
154         return before.andThen(this);
155     }
156 
157     default OpTransformer andThen(OpTransformer after) {
158         if (after == NOOP_TRANSFORMER) {
159             return this;
160         } else if (this == NOOP_TRANSFORMER) {
161             return after;
162         } else {
163             return (bb, o) -> {
164                 Block.Builder nbb = acceptOp(bb, o);
165                 if (nbb != null) {
166                     return after.acceptOp(nbb, o);
167                 } else {
168                     // @@@ This does not currently occur
169                     return null;
170                 }
171             };
172         }
173     }
174 }