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