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 package hat.optools;
 26 
 27 import hat.buffer.Buffer;
 28 
 29 import hat.ifacemapper.MappableIface;
 30 import jdk.incubator.code.Block;
 31 import jdk.incubator.code.Body;
 32 import jdk.incubator.code.Op;
 33 import jdk.incubator.code.TypeElement;
 34 import jdk.incubator.code.Value;
 35 import jdk.incubator.code.op.CoreOp;
 36 import jdk.incubator.code.op.ExtendedOp;
 37 import jdk.incubator.code.type.ClassType;
 38 import jdk.incubator.code.type.JavaType;
 39 
 40 import java.lang.invoke.MethodHandle;
 41 import java.lang.invoke.MethodHandles;
 42 import java.lang.reflect.Type;
 43 import java.util.ArrayList;
 44 import java.util.List;
 45 import java.util.function.Consumer;
 46 import java.util.stream.Stream;
 47 
 48 public class OpWrapper<T extends Op> {
 49     @SuppressWarnings("unchecked")
 50     public static <O extends Op, OW extends OpWrapper<O>> OW wrap(MethodHandles.Lookup lookup,O op) {
 51         // We have one special case
 52         // This is possibly a premature optimization. But it allows us to treat vardeclarations differently from params.
 53         if (op instanceof CoreOp.VarOp varOp && !varOp.isUninitialized()) {
 54             // this gets called a lot and we can't wrap yet or we recurse so we
 55             // use the raw model. Basically we want a different wrapper for VarDeclarations
 56             // which  relate to func parameters.
 57             // This saves us asking each time if a var is indeed a func param.
 58 
 59             if (varOp.operands().getFirst() instanceof Block.Parameter parameter &&
 60                     parameter.invokableOperation() instanceof CoreOp.FuncOp funcOp) {
 61                 return (OW) new VarFuncDeclarationOpWrapper(lookup,varOp, funcOp, parameter);
 62             }
 63         }
 64         return switch (op) {
 65             case CoreOp.ModuleOp $ -> (OW) new ModuleOpWrapper(lookup, $);
 66             case ExtendedOp.JavaForOp $ -> (OW) new ForOpWrapper(lookup, $);
 67             case ExtendedOp.JavaWhileOp $ -> (OW) new WhileOpWrapper(lookup, $);
 68             case ExtendedOp.JavaIfOp $ -> (OW) new IfOpWrapper(lookup, $);
 69             case CoreOp.NotOp $ -> (OW) new UnaryArithmeticOrLogicOpWrapper(lookup, $);
 70             case CoreOp.NegOp $ -> (OW) new UnaryArithmeticOrLogicOpWrapper(lookup, $);
 71             case CoreOp.BinaryOp $ -> (OW) new BinaryArithmeticOrLogicOperation(lookup, $);
 72             case CoreOp.BinaryTestOp $ -> (OW) new BinaryTestOpWrapper(lookup, $);
 73             case CoreOp.FuncOp $ -> (OW) new FuncOpWrapper(lookup, $);
 74             case CoreOp.VarOp $ -> (OW) new VarDeclarationOpWrapper(lookup, $);
 75             case CoreOp.YieldOp $ -> (OW) new YieldOpWrapper(lookup, $);
 76             case CoreOp.FuncCallOp $ -> (OW) new FuncCallOpWrapper(lookup, $);
 77             case CoreOp.ConvOp $ -> (OW) new ConvOpWrapper(lookup, $);
 78             case CoreOp.ConstantOp $ -> (OW) new ConstantOpWrapper(lookup, $);
 79             case CoreOp.ReturnOp $ -> (OW) new ReturnOpWrapper(lookup, $);
 80             case CoreOp.VarAccessOp.VarStoreOp $ -> (OW) new VarStoreOpWrapper(lookup, $);
 81             case CoreOp.VarAccessOp.VarLoadOp $ -> (OW) new VarLoadOpWrapper(lookup, $);
 82             case CoreOp.FieldAccessOp.FieldStoreOp $ -> (OW) new FieldStoreOpWrapper(lookup, $);
 83             case CoreOp.FieldAccessOp.FieldLoadOp $ -> (OW) new FieldLoadOpWrapper(lookup, $);
 84             case CoreOp.InvokeOp $ -> (OW) new InvokeOpWrapper(lookup, $);
 85             case CoreOp.TupleOp $ -> (OW) new TupleOpWrapper(lookup, $);
 86             case CoreOp.LambdaOp $ -> (OW) new LambdaOpWrapper(lookup, $);
 87             case ExtendedOp.JavaConditionalOp $ -> (OW) new LogicalOpWrapper(lookup, $);
 88             case ExtendedOp.JavaConditionalExpressionOp $ -> (OW) new TernaryOpWrapper(lookup, $);
 89             case ExtendedOp.JavaLabeledOp $ -> (OW) new JavaLabeledOpWrapper(lookup, $);
 90             case ExtendedOp.JavaBreakOp $ -> (OW) new JavaBreakOpWrapper(lookup, $);
 91             case ExtendedOp.JavaContinueOp $ -> (OW) new JavaContinueOpWrapper(lookup, $);
 92             default -> (OW) new OpWrapper<>(lookup,op);
 93         };
 94     }
 95 
 96     private final T op;
 97 public MethodHandles.Lookup lookup;
 98     OpWrapper( MethodHandles.Lookup lookup,T op) {
 99         this.lookup= lookup;
100         this.op = op;
101 
102     }
103 
104     public T op() {
105         return (T) op;
106     }
107 
108     public Body firstBody() {
109         if (op.bodies().isEmpty()) {
110             throw new IllegalStateException("no body!");
111         }
112         return op.bodies().getFirst();
113     }
114 
115     public Body onlyBody() {
116         if (op.bodies().size() != 1) {
117             throw new IllegalStateException("not the only body!");
118         }
119         return firstBody();
120     }
121 
122     public void onlyBody(Consumer<BodyWrapper> bodyWrapperConsumer) {
123         bodyWrapperConsumer.accept(new BodyWrapper(onlyBody()));
124     }
125 
126     public final Stream<Body> bodies() {
127         return op.bodies().stream();
128     }
129 
130     public void selectOnlyBlockOfOnlyBody(Consumer<BlockWrapper> blockWrapperConsumer) {
131         onlyBody(w -> {
132             w.onlyBlock(blockWrapperConsumer);
133         });
134     }
135 
136     public void selectCalls(Consumer<InvokeOpWrapper> consumer) {
137         this.op.traverse(null, (map, op) -> {
138             if (op instanceof CoreOp.InvokeOp invokeOp) {
139                 consumer.accept(wrap(lookup,invokeOp));
140             }
141             return map;
142         });
143     }
144     public void selectAssignments(Consumer<VarOpWrapper> consumer) {
145         this.op.traverse(null, (map, op) -> {
146             if (op instanceof CoreOp.VarOp varOp) {
147                 consumer.accept(wrap(lookup,varOp));
148             }
149             return map;
150         });
151     }
152 
153     public BlockWrapper parentBlock() {
154         return new BlockWrapper(op.parentBlock());
155     }
156 
157     public BodyWrapper parentBodyOfParentBlock() {
158         return parentBlock().parentBody();
159     }
160 
161 
162     public Op.Result operandNAsResult(int i) {
163         if (operandNAsValue(i) instanceof Op.Result result) {
164             return result;
165         } else {
166             return null;
167         }
168     }
169 
170     public Value operandNAsValue(int i) {
171         return hasOperandN(i) ? op().operands().get(i) : null;
172     }
173 
174     public boolean hasOperandN(int i) {
175         return operandCount() > i;
176     }
177 
178     public int operandCount() {
179         return op().operands().size();
180     }
181 
182     public boolean hasOperands() {
183         return !hasNoOperands();
184     }
185 
186     public boolean hasNoOperands() {
187         return operands().isEmpty();
188     }
189 
190     public List<Value> operands() {
191         return op.operands();
192     }
193 
194     public Body bodyN(int i) {
195         return op().bodies().get(i);
196     }
197 
198     public boolean hasBodyN(int i) {
199         return op().bodies().size() > i;
200     }
201 
202     public Block firstBlockOfBodyN(int i) {
203         return bodyN(i).entryBlock();
204     }
205 
206     public Block firstBlockOfFirstBody() {
207         return op().bodies().getFirst().entryBlock();
208     }
209 
210     public String toText() {
211         return op().toText();
212     }
213 
214     public Stream<OpWrapper<?>> wrappedOpStream(Block block) {
215         return block.ops().stream().map(o->wrap(lookup,o));
216     }
217 
218     public Stream<OpWrapper<?>> wrappedYieldOpStream(Block block) {
219         return wrappedOpStream(block).filter(wrapped -> wrapped instanceof YieldOpWrapper);
220     }
221 
222     private Stream<OpWrapper<?>> roots(Block block) {
223         var rootSet = RootSet.getRootSet(block.ops().stream());
224         return block.ops().stream().filter(rootSet::contains).map(o->wrap(lookup,o));
225     }
226 
227     private Stream<OpWrapper<?>> rootsWithoutVarFuncDeclarations(Block block) {
228         return roots(block).filter(w -> !(w instanceof VarFuncDeclarationOpWrapper));
229     }
230 
231     private Stream<OpWrapper<?>> rootsWithoutVarFuncDeclarationsOrYields(Block block) {
232         return rootsWithoutVarFuncDeclarations(block).filter(w -> !(w instanceof YieldOpWrapper));
233     }
234 
235     public Stream<OpWrapper<?>> wrappedRootOpStream(Block block) {
236         return rootsWithoutVarFuncDeclarationsOrYields(block);
237     }
238 
239     public Stream<OpWrapper<?>> wrappedRootOpStreamSansFinalContinue(Block block) {
240         var list = new ArrayList<>(rootsWithoutVarFuncDeclarationsOrYields(block).toList());
241         if (list.getLast() instanceof JavaContinueOpWrapper javaContinueOpWrapper) {
242             list.removeLast();
243         }
244         return list.stream();
245     }
246 
247     public Op.Result result() {
248         return op.result();
249     }
250 
251     public TypeElement resultType() {
252         return op.resultType();
253     }
254     public  static boolean isIfaceUsingLookup(MethodHandles.Lookup lookup,JavaType javaType) {
255         return  (isAssignableUsingLookup(lookup,javaType, MappableIface.class));
256     }
257     public  boolean isIface(JavaType javaType) {
258         return  (isAssignable(javaType, MappableIface.class));
259     }
260 
261     public  static Type classTypeToTypeUsingLookup(MethodHandles.Lookup lookup,ClassType classType) {
262         Type javaTypeClass = null;
263         try {
264             javaTypeClass = classType.resolve(lookup);
265         } catch (ReflectiveOperationException e) {
266             throw new RuntimeException(e);
267         }
268         return javaTypeClass;
269 
270     }
271     public  Type classTypeToType(ClassType classType) {
272        return classTypeToTypeUsingLookup(lookup,classType);
273     }
274     public  static boolean isAssignableUsingLookup(MethodHandles.Lookup lookup,JavaType javaType, Class<?> ... classes) {
275         if (javaType instanceof ClassType classType) {
276             Type type = classTypeToTypeUsingLookup(lookup,classType);
277             for (Class<?> clazz : classes) {
278                 if (clazz.isAssignableFrom((Class<?>) type)) {
279                     return true;
280                 }
281             }
282         }
283         return false;
284 
285     }
286     public  boolean isAssignable(JavaType javaType, Class<?> ... classes) {
287        return isAssignableUsingLookup(lookup,javaType,classes);
288 
289     }
290 }