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 }