001/* 002 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.replacements; 024 025import static jdk.internal.jvmci.meta.MetaUtil.*; 026 027import java.lang.reflect.*; 028import java.util.*; 029 030import jdk.internal.jvmci.common.*; 031 032import com.oracle.graal.debug.*; 033import com.oracle.graal.debug.internal.*; 034 035import jdk.internal.jvmci.meta.*; 036 037import com.oracle.graal.api.replacements.*; 038import com.oracle.graal.compiler.common.spi.*; 039import com.oracle.graal.compiler.common.type.*; 040import com.oracle.graal.graph.*; 041import com.oracle.graal.graph.Node.ConstantNodeParameter; 042import com.oracle.graal.graph.Node.InjectedNodeParameter; 043import com.oracle.graal.graph.Node.NodeIntrinsic; 044import com.oracle.graal.nodes.*; 045import com.oracle.graal.nodes.calc.*; 046import com.oracle.graal.nodes.extended.*; 047import com.oracle.graal.nodes.java.*; 048import com.oracle.graal.nodes.spi.*; 049import com.oracle.graal.nodes.util.*; 050import com.oracle.graal.phases.*; 051 052/** 053 * Replaces calls to {@link NodeIntrinsic}s with nodes and calls to methods annotated with 054 * {@link Fold} with the result of invoking the annotated method via reflection. 055 */ 056public class NodeIntrinsificationPhase extends Phase { 057 058 private final MetaAccessProvider metaAccess; 059 private final ConstantReflectionProvider constantReflection; 060 private final SnippetReflectionProvider snippetReflection; 061 private final ForeignCallsProvider foreignCalls; 062 private final StampProvider stampProvider; 063 064 public NodeIntrinsificationPhase(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, SnippetReflectionProvider snippetReflection, ForeignCallsProvider foreignCalls, 065 StampProvider stampProvider) { 066 this.metaAccess = metaAccess; 067 this.constantReflection = constantReflection; 068 this.snippetReflection = snippetReflection; 069 this.foreignCalls = foreignCalls; 070 this.stampProvider = stampProvider; 071 } 072 073 @Override 074 protected void run(StructuredGraph graph) { 075 ArrayList<Node> cleanUpReturnList = new ArrayList<>(); 076 for (MethodCallTargetNode node : graph.getNodes(MethodCallTargetNode.TYPE)) { 077 tryIntrinsify(node, cleanUpReturnList); 078 } 079 080 for (Node node : cleanUpReturnList) { 081 cleanUpReturnCheckCast(node); 082 } 083 } 084 085 protected boolean tryIntrinsify(MethodCallTargetNode methodCallTargetNode, List<Node> cleanUpReturnList) { 086 ResolvedJavaMethod target = methodCallTargetNode.targetMethod(); 087 ResolvedJavaType declaringClass = target.getDeclaringClass(); 088 StructuredGraph graph = methodCallTargetNode.graph(); 089 090 NodeIntrinsic intrinsic = getIntrinsic(target); 091 if (intrinsic != null) { 092 Stamp stamp = methodCallTargetNode.invoke().asNode().stamp(); 093 Node newInstance = createIntrinsicNode(methodCallTargetNode.arguments(), stamp, target, graph, intrinsic); 094 if (newInstance == null) { 095 return false; 096 } 097 098 // Replace the invoke with the new node. 099 newInstance = graph.addOrUnique(newInstance); 100 methodCallTargetNode.invoke().intrinsify(newInstance); 101 102 // Clean up checkcast instructions inserted by javac if the return type is generic. 103 cleanUpReturnList.add(newInstance); 104 } else if (isFoldable(target)) { 105 ResolvedJavaType[] parameterTypes = resolveJavaTypes(target.toParameterTypes(), declaringClass); 106 JavaConstant constant = tryFold(methodCallTargetNode.arguments(), parameterTypes, target); 107 if (constant != null && constant.equals(COULD_NOT_FOLD)) { 108 return false; 109 } 110 111 if (constant != null) { 112 // Replace the invoke with the result of the call 113 ConstantNode node = ConstantNode.forConstant(constant, metaAccess, methodCallTargetNode.graph()); 114 methodCallTargetNode.invoke().intrinsify(node); 115 116 // Clean up checkcast instructions inserted by javac if the return type is generic. 117 cleanUpReturnList.add(node); 118 } else { 119 // Remove the invoke 120 methodCallTargetNode.invoke().intrinsify(null); 121 } 122 } 123 return true; 124 } 125 126 public static final JavaConstant COULD_NOT_FOLD = new PrimitiveConstant(Kind.Illegal, 100) { 127 @Override 128 public boolean equals(Object o) { 129 return this == o; 130 } 131 }; 132 133 public JavaConstant tryFold(List<ValueNode> args, ResolvedJavaType[] parameterTypes, ResolvedJavaMethod target) { 134 JavaConstant[] reflectArgs = (JavaConstant[]) prepareArguments(args, parameterTypes, target, true); 135 if (reflectArgs == null) { 136 return COULD_NOT_FOLD; 137 } 138 JavaConstant receiver = null; 139 if (!target.isStatic()) { 140 receiver = reflectArgs[0]; 141 reflectArgs = Arrays.copyOfRange(reflectArgs, 1, reflectArgs.length); 142 } 143 144 // Call the method 145 return target.invoke(receiver, reflectArgs); 146 } 147 148 private static boolean areAllConstant(List<ValueNode> arguments) { 149 for (ValueNode arg : arguments) { 150 if (!arg.isConstant()) { 151 return false; 152 } 153 } 154 return true; 155 } 156 157 /** 158 * Attempts to create a node to replace a call to a {@link NodeIntrinsic} annotated method. 159 * 160 * @param arguments the arguments of the call 161 * @param stamp the stamp to use for the returned node 162 * @param method the method annotated with {@link NodeIntrinsic} 163 * @param graph the graph into which the created node will be added 164 * @return a {@link ConstantNode} if the intrinsic could be 165 * {@linkplain NodeIntrinsic#foldable() folded}, {@code null} if intrinsification could 166 * not (yet) be performed, otherwise the node representing the intrinsic 167 */ 168 public ValueNode createIntrinsicNode(List<ValueNode> arguments, Stamp stamp, ResolvedJavaMethod method, StructuredGraph graph, NodeIntrinsic intrinsic) { 169 assert method.getAnnotation(Fold.class) == null; 170 assert method.isStatic() : "node intrinsic must be static: " + method; 171 172 ResolvedJavaType[] parameterTypes = resolveJavaTypes(method.toParameterTypes(), method.getDeclaringClass()); 173 174 if (intrinsic.foldable() && areAllConstant(arguments)) { 175 JavaConstant res = tryFold(arguments, parameterTypes, method); 176 if (!res.equals(COULD_NOT_FOLD)) { 177 return ConstantNode.forConstant(res, metaAccess); 178 } 179 } 180 181 // Prepare the arguments for the reflective constructor call on the node class. 182 Object[] nodeConstructorArguments = prepareArguments(arguments, parameterTypes, method, false); 183 if (nodeConstructorArguments == null) { 184 return null; 185 } 186 187 // Create the new node instance. 188 ResolvedJavaType c = getNodeClass(method, intrinsic); 189 return createNodeInstance(graph, c, parameterTypes, stamp, intrinsic.setStampFromReturnType(), nodeConstructorArguments); 190 } 191 192 /** 193 * Permits a subclass to override the default definition of "intrinsic". 194 */ 195 public NodeIntrinsic getIntrinsic(ResolvedJavaMethod method) { 196 return method.getAnnotation(Node.NodeIntrinsic.class); 197 } 198 199 /** 200 * Permits a subclass to override the default definition of "foldable". 201 */ 202 public boolean isFoldable(ResolvedJavaMethod method) { 203 return method.getAnnotation(Fold.class) != null; 204 } 205 206 /** 207 * Converts the arguments of an invoke node to object values suitable for use as the arguments 208 * to a reflective invocation of a Java constructor or method. 209 * 210 * @param folding specifies if the invocation is for handling a {@link Fold} annotation 211 * @return the arguments for the reflective invocation or null if an argument of {@code invoke} 212 * that is expected to be constant isn't 213 */ 214 private Object[] prepareArguments(List<ValueNode> arguments, ResolvedJavaType[] parameterTypes, ResolvedJavaMethod target, boolean folding) { 215 Object[] reflectionCallArguments = folding ? new JavaConstant[arguments.size()] : new Object[arguments.size()]; 216 for (int i = 0; i < reflectionCallArguments.length; ++i) { 217 int parameterIndex = i; 218 if (!target.isStatic()) { 219 parameterIndex--; 220 } 221 ValueNode argument = arguments.get(i); 222 if (folding || target.getParameterAnnotation(ConstantNodeParameter.class, parameterIndex) != null) { 223 if (!(argument instanceof ConstantNode)) { 224 return null; 225 } 226 ConstantNode constantNode = (ConstantNode) argument; 227 Constant constant = constantNode.asConstant(); 228 /* 229 * For intrinsification (but not for folding) if we have a Class<?> object we want 230 * the corresponding ResolvedJavaType. 231 */ 232 ResolvedJavaType type = folding ? null : constantReflection.asJavaType(constant); 233 Object arg; 234 if (type != null) { 235 /* If we found such a type then it's our arg */ 236 arg = type; 237 parameterTypes[i] = metaAccess.lookupJavaType(ResolvedJavaType.class); 238 } else { 239 JavaConstant javaConstant = (JavaConstant) constant; 240 if (folding) { 241 /* For folding we want JavaConstants */ 242 arg = javaConstant; 243 } else { 244 /* For intrinsification we want want corresponding objects */ 245 if (parameterTypes[i].getKind() == Kind.Boolean) { 246 arg = Boolean.valueOf(javaConstant.asInt() != 0); 247 } else if (parameterTypes[i].getKind() == Kind.Byte) { 248 arg = Byte.valueOf((byte) javaConstant.asInt()); 249 } else if (parameterTypes[i].getKind() == Kind.Short) { 250 arg = Short.valueOf((short) javaConstant.asInt()); 251 } else if (parameterTypes[i].getKind() == Kind.Char) { 252 arg = Character.valueOf((char) javaConstant.asInt()); 253 } else if (parameterTypes[i].getKind() == Kind.Object) { 254 arg = snippetReflection.asObject(parameterTypes[i], javaConstant); 255 } else { 256 arg = javaConstant.asBoxedPrimitive(); 257 } 258 } 259 } 260 261 assert folding || !(arg instanceof JavaConstant); 262 reflectionCallArguments[i] = arg; 263 } else { 264 reflectionCallArguments[i] = argument; 265 parameterTypes[i] = metaAccess.lookupJavaType(ValueNode.class); 266 } 267 } 268 return reflectionCallArguments; 269 } 270 271 public ResolvedJavaType getNodeClass(ResolvedJavaMethod target, NodeIntrinsic intrinsic) { 272 ResolvedJavaType result; 273 if (intrinsic.value() == NodeIntrinsic.class) { 274 result = target.getDeclaringClass(); 275 } else { 276 result = metaAccess.lookupJavaType(intrinsic.value()); 277 } 278 assert metaAccess.lookupJavaType(ValueNode.class).isAssignableFrom(result) : "Node intrinsic class " + result.toJavaName(false) + " derived from @" + NodeIntrinsic.class.getSimpleName() + 279 " annotation on " + target.format("%H.%n(%p)") + " is not a subclass of " + ValueNode.class; 280 return result; 281 } 282 283 protected ValueNode createNodeInstance(StructuredGraph graph, ResolvedJavaType nodeClass, ResolvedJavaType[] parameterTypes, Stamp invokeStamp, boolean setStampFromReturnType, 284 Object[] nodeConstructorArguments) { 285 ResolvedJavaMethod constructor = null; 286 Object[] arguments = null; 287 288 for (ResolvedJavaMethod c : nodeClass.getDeclaredConstructors()) { 289 Object[] match = match(graph, invokeStamp, c, parameterTypes, nodeConstructorArguments); 290 291 if (match != null) { 292 if (constructor == null) { 293 constructor = c; 294 arguments = match; 295 if (!Debug.isEnabled()) { 296 // Don't verify there's a unique match in non-debug mode 297 break; 298 } 299 } else { 300 throw new JVMCIError("Found multiple constructors in %s compatible with signature %s: %s, %s", nodeClass.toJavaName(), sigString(parameterTypes), constructor, c); 301 } 302 } 303 } 304 if (constructor == null) { 305 throw new JVMCIError("Could not find constructor in %s compatible with signature %s", nodeClass.toJavaName(), sigString(parameterTypes)); 306 } 307 308 try { 309 ValueNode intrinsicNode = (ValueNode) invokeConstructor(constructor, arguments); 310 311 if (setStampFromReturnType) { 312 intrinsicNode.setStamp(invokeStamp); 313 } 314 return intrinsicNode; 315 } catch (Exception e) { 316 throw new RuntimeException(constructor + Arrays.toString(nodeConstructorArguments), e); 317 } 318 } 319 320 protected Object invokeConstructor(ResolvedJavaMethod constructor, Object[] arguments) { 321 return snippetReflection.invoke(constructor, null, arguments); 322 } 323 324 private static String sigString(ResolvedJavaType[] types) { 325 StringBuilder sb = new StringBuilder("("); 326 for (int i = 0; i < types.length; i++) { 327 if (i != 0) { 328 sb.append(", "); 329 } 330 sb.append(types[i].toJavaName()); 331 } 332 return sb.append(")").toString(); 333 } 334 335 private static boolean checkNoMoreInjected(ResolvedJavaMethod c, int start) { 336 int count = c.getSignature().getParameterCount(false); 337 for (int i = start; i < count; i++) { 338 if (c.getParameterAnnotation(InjectedNodeParameter.class, i) != null) { 339 throw new JVMCIError("Injected parameter %d of type %s must precede all non-injected parameters of %s", i, 340 c.getSignature().getParameterType(i, c.getDeclaringClass()).toJavaName(false), c.format("%H.%n(%p)")); 341 } 342 } 343 return true; 344 } 345 346 private Object[] match(StructuredGraph graph, Stamp invokeStamp, ResolvedJavaMethod c, ResolvedJavaType[] parameterTypes, Object[] nodeConstructorArguments) { 347 Object[] arguments = null; 348 Object[] injected = null; 349 350 ResolvedJavaType[] signature = resolveJavaTypes(c.getSignature().toParameterTypes(null), c.getDeclaringClass()); 351 for (int i = 0; i < signature.length; i++) { 352 if (c.getParameterAnnotation(InjectedNodeParameter.class, i) != null) { 353 injected = injected == null ? new Object[1] : Arrays.copyOf(injected, injected.length + 1); 354 Object injectedParameter = snippetReflection.getInjectedNodeIntrinsicParameter(signature[i]); 355 if (injectedParameter != null) { 356 injected[injected.length - 1] = injectedParameter; 357 } else if (signature[i].equals(metaAccess.lookupJavaType(MetaAccessProvider.class))) { 358 injected[injected.length - 1] = metaAccess; 359 } else if (signature[i].equals(metaAccess.lookupJavaType(StructuredGraph.class))) { 360 injected[injected.length - 1] = graph; 361 } else if (signature[i].equals(metaAccess.lookupJavaType(ForeignCallsProvider.class))) { 362 injected[injected.length - 1] = foreignCalls; 363 } else if (signature[i].equals(metaAccess.lookupJavaType(SnippetReflectionProvider.class))) { 364 injected[injected.length - 1] = snippetReflection; 365 } else if (signature[i].isAssignableFrom(metaAccess.lookupJavaType(Stamp.class))) { 366 injected[injected.length - 1] = invokeStamp; 367 } else if (signature[i].isAssignableFrom(metaAccess.lookupJavaType(StampProvider.class))) { 368 injected[injected.length - 1] = stampProvider; 369 } else { 370 throw new JVMCIError("Cannot handle injected argument of type %s in %s", signature[i].toJavaName(), c.format("%H.%n(%p)")); 371 } 372 } else { 373 assert checkNoMoreInjected(c, i); 374 break; 375 } 376 } 377 if (injected != null) { 378 // Chop injected arguments from signature 379 signature = Arrays.copyOfRange(signature, injected.length, signature.length); 380 } 381 382 if (Arrays.equals(parameterTypes, signature)) { 383 // Exact match 384 arguments = nodeConstructorArguments; 385 386 } else if (signature.length > 0 && signature[signature.length - 1].isArray()) { 387 // Last constructor parameter is an array, so check if we have a vararg match 388 int fixedArgs = signature.length - 1; 389 if (parameterTypes.length < fixedArgs) { 390 return null; 391 } 392 for (int i = 0; i < fixedArgs; i++) { 393 if (!parameterTypes[i].equals(signature[i])) { 394 return null; 395 } 396 } 397 398 ResolvedJavaType componentType = signature[fixedArgs].getComponentType(); 399 assert componentType != null; 400 for (int i = fixedArgs; i < nodeConstructorArguments.length; i++) { 401 if (!parameterTypes[i].equals(componentType)) { 402 return null; 403 } 404 } 405 arguments = Arrays.copyOf(nodeConstructorArguments, fixedArgs + 1); 406 arguments[fixedArgs] = snippetReflection.newArray(componentType, nodeConstructorArguments.length - fixedArgs); 407 408 Object varargs = arguments[fixedArgs]; 409 for (int i = fixedArgs; i < nodeConstructorArguments.length; i++) { 410 if (componentType.isPrimitive()) { 411 Array.set(varargs, i - fixedArgs, nodeConstructorArguments[i]); 412 } else { 413 ((Object[]) varargs)[i - fixedArgs] = nodeConstructorArguments[i]; 414 } 415 } 416 } else { 417 return null; 418 } 419 420 if (injected != null) { 421 Object[] copy = new Object[injected.length + arguments.length]; 422 System.arraycopy(injected, 0, copy, 0, injected.length); 423 System.arraycopy(arguments, 0, copy, injected.length, arguments.length); 424 arguments = copy; 425 } 426 return arguments; 427 } 428 429 private static String sourceLocation(Node n) { 430 String loc = GraphUtil.approxSourceLocation(n); 431 return loc == null ? "<unknown>" : loc; 432 } 433 434 public void cleanUpReturnCheckCast(Node newInstance) { 435 if (newInstance instanceof ValueNode && (((ValueNode) newInstance).getKind() != Kind.Object || ((ValueNode) newInstance).stamp() == StampFactory.forNodeIntrinsic())) { 436 StructuredGraph graph = (StructuredGraph) newInstance.graph(); 437 for (CheckCastNode checkCastNode : newInstance.usages().filter(CheckCastNode.class).snapshot()) { 438 for (Node checkCastUsage : checkCastNode.usages().snapshot()) { 439 checkCheckCastUsage(graph, newInstance, checkCastNode, checkCastUsage); 440 } 441 GraphUtil.unlinkFixedNode(checkCastNode); 442 GraphUtil.killCFG(checkCastNode); 443 } 444 } 445 } 446 447 private static void checkCheckCastUsage(StructuredGraph graph, Node intrinsifiedNode, Node input, Node usage) { 448 if (usage instanceof ValueAnchorNode) { 449 ValueAnchorNode valueAnchorNode = (ValueAnchorNode) usage; 450 valueAnchorNode.removeAnchoredNode(); 451 Debug.log("%s: Removed a ValueAnchor input", Debug.contextSnapshot(JavaMethod.class)); 452 } else if (usage instanceof UnboxNode) { 453 UnboxNode unbox = (UnboxNode) usage; 454 unbox.replaceAtUsages(intrinsifiedNode); 455 graph.removeFixed(unbox); 456 Debug.log("%s: Removed an UnboxNode", Debug.contextSnapshot(JavaMethod.class)); 457 } else if (usage instanceof UnsafeStoreNode) { 458 UnsafeStoreNode store = (UnsafeStoreNode) usage; 459 store.replaceFirstInput(input, intrinsifiedNode); 460 } else if (usage instanceof LoadFieldNode) { 461 LoadFieldNode load = (LoadFieldNode) usage; 462 load.replaceAtUsages(intrinsifiedNode); 463 graph.removeFixed(load); 464 } else if (usage instanceof MethodCallTargetNode) { 465 MethodCallTargetNode checkCastCallTarget = (MethodCallTargetNode) usage; 466 assert checkCastCallTarget.targetMethod().getAnnotation(NodeIntrinsic.class) != null : "checkcast at " + sourceLocation(input) + 467 " not used by an unboxing method or node intrinsic, but by a call at " + sourceLocation(checkCastCallTarget.usages().first()) + " to " + checkCastCallTarget.targetMethod(); 468 usage.replaceFirstInput(input, intrinsifiedNode); 469 Debug.log("%s: Checkcast used in an other node intrinsic", Debug.contextSnapshot(JavaMethod.class)); 470 } else if (usage instanceof FrameState) { 471 usage.replaceFirstInput(input, null); 472 Debug.log("%s: Checkcast used in a FS", Debug.contextSnapshot(JavaMethod.class)); 473 } else if (usage instanceof ReturnNode && ((ValueNode) intrinsifiedNode).stamp() == StampFactory.forNodeIntrinsic()) { 474 usage.replaceFirstInput(input, intrinsifiedNode); 475 Debug.log("%s: Checkcast used in a return with forNodeIntrinsic stamp", Debug.contextSnapshot(JavaMethod.class)); 476 } else if (usage instanceof IsNullNode) { 477 if (!usage.hasNoUsages()) { 478 assert usage.getUsageCount() == 1 && usage.usages().first().predecessor() == input : usage + " " + input; 479 graph.replaceFloating((FloatingNode) usage, LogicConstantNode.contradiction(graph)); 480 Debug.log("%s: Replaced IsNull with false", Debug.contextSnapshot(JavaMethod.class)); 481 } else { 482 // Removed as usage of a GuardingPiNode 483 } 484 } else if (usage instanceof ProxyNode) { 485 ProxyNode proxy = (ProxyNode) usage; 486 assert proxy instanceof ValueProxyNode; 487 ProxyNode newProxy = ProxyNode.forValue((ValueNode) intrinsifiedNode, proxy.proxyPoint(), graph); 488 for (Node proxyUsage : usage.usages().snapshot()) { 489 checkCheckCastUsage(graph, newProxy, proxy, proxyUsage); 490 } 491 } else if (usage instanceof PiNode) { 492 for (Node piUsage : usage.usages().snapshot()) { 493 checkCheckCastUsage(graph, intrinsifiedNode, usage, piUsage); 494 } 495 } else { 496 DebugScope.forceDump(graph, "exception"); 497 assert false : sourceLocation(usage) + " has unexpected usage " + usage + " of checkcast " + input + " at " + sourceLocation(input); 498 } 499 } 500}