001/* 002 * Copyright (c) 2015, 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 com.oracle.graal.replacements.NodeIntrinsificationPhase.*; 026import static jdk.internal.jvmci.meta.MetaUtil.*; 027 028import java.util.*; 029 030import jdk.internal.jvmci.common.*; 031import jdk.internal.jvmci.meta.*; 032 033import com.oracle.graal.api.replacements.*; 034import com.oracle.graal.compiler.common.type.*; 035import com.oracle.graal.debug.*; 036import com.oracle.graal.graph.Node.NodeIntrinsic; 037import com.oracle.graal.graphbuilderconf.*; 038import com.oracle.graal.nodeinfo.*; 039import com.oracle.graal.nodeinfo.StructuralInput.MarkerType; 040import com.oracle.graal.nodes.*; 041import com.oracle.graal.nodes.extended.*; 042import com.oracle.graal.word.*; 043 044/** 045 * An {@link NodePlugin} that handles methods annotated by {@link Fold} and {@link NodeIntrinsic}. 046 */ 047public class NodeIntrinsificationPlugin implements NodePlugin { 048 protected final NodeIntrinsificationPhase nodeIntrinsification; 049 private final WordTypes wordTypes; 050 private final ResolvedJavaType structuralInputType; 051 private final boolean mustIntrinsify; 052 053 public NodeIntrinsificationPlugin(MetaAccessProvider metaAccess, NodeIntrinsificationPhase nodeIntrinsification, WordTypes wordTypes, boolean mustIntrinsify) { 054 this.nodeIntrinsification = nodeIntrinsification; 055 this.wordTypes = wordTypes; 056 this.mustIntrinsify = mustIntrinsify; 057 this.structuralInputType = metaAccess.lookupJavaType(StructuralInput.class); 058 } 059 060 /** 061 * Calls in replacements to methods matching one of these filters are elided. Only void methods 062 * are considered for elision. The use of "snippets" in name of the variable and system property 063 * is purely for legacy reasons. 064 */ 065 private static final MethodFilter[] MethodsElidedInSnippets = getMethodsElidedInSnippets(); 066 067 private static MethodFilter[] getMethodsElidedInSnippets() { 068 String commaSeparatedPatterns = System.getProperty("graal.MethodsElidedInSnippets"); 069 if (commaSeparatedPatterns != null) { 070 return MethodFilter.parse(commaSeparatedPatterns); 071 } 072 return null; 073 } 074 075 @Override 076 public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 077 NodeIntrinsic intrinsic = nodeIntrinsification.getIntrinsic(method); 078 if (intrinsic != null) { 079 Signature sig = method.getSignature(); 080 Kind returnKind = sig.getReturnKind(); 081 Stamp stamp = StampFactory.forKind(returnKind); 082 if (returnKind == Kind.Object) { 083 JavaType returnType = sig.getReturnType(method.getDeclaringClass()); 084 if (returnType instanceof ResolvedJavaType) { 085 ResolvedJavaType resolvedReturnType = (ResolvedJavaType) returnType; 086 if (wordTypes.isWord(resolvedReturnType)) { 087 stamp = wordTypes.getWordStamp(resolvedReturnType); 088 } else { 089 stamp = StampFactory.declared(resolvedReturnType); 090 } 091 } 092 } 093 094 boolean result = processNodeIntrinsic(b, method, intrinsic, Arrays.asList(args), returnKind, stamp); 095 if (!result && mustIntrinsify) { 096 reportIntrinsificationFailure(b, method, args); 097 } 098 return result; 099 100 } else if (nodeIntrinsification.isFoldable(method)) { 101 ResolvedJavaType[] parameterTypes = resolveJavaTypes(method.toParameterTypes(), method.getDeclaringClass()); 102 JavaConstant constant = nodeIntrinsification.tryFold(Arrays.asList(args), parameterTypes, method); 103 if (!COULD_NOT_FOLD.equals(constant)) { 104 if (constant != null) { 105 // Replace the invoke with the result of the call 106 b.push(method.getSignature().getReturnKind(), ConstantNode.forConstant(constant, b.getMetaAccess(), b.getGraph())); 107 } else { 108 // This must be a void invoke 109 assert method.getSignature().getReturnKind() == Kind.Void; 110 } 111 return true; 112 } else if (mustIntrinsify) { 113 reportIntrinsificationFailure(b, method, args); 114 } 115 116 } else if (MethodsElidedInSnippets != null) { 117 if (MethodFilter.matches(MethodsElidedInSnippets, method)) { 118 if (method.getSignature().getReturnKind() != Kind.Void) { 119 throw new JVMCIError("Cannot elide non-void method " + method.format("%H.%n(%p)")); 120 } 121 return true; 122 } 123 } 124 return false; 125 } 126 127 private static boolean reportIntrinsificationFailure(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 128 StringBuilder msg = new StringBuilder(); 129 msg.append("Call in ").append(b.getMethod().format("%H.%n(%p)")); 130 msg.append(" to ").append(method.format("%H.%n(%p)")); 131 msg.append(" cannot be intrisfied or folded, probably because an argument is not a constant. Arguments: "); 132 String sep = ""; 133 for (ValueNode node : args) { 134 msg.append(sep).append(node.toString()); 135 sep = ", "; 136 } 137 throw new JVMCIError(msg.toString()); 138 } 139 140 private InputType getInputType(ResolvedJavaType type) { 141 if (type != null && structuralInputType.isAssignableFrom(type)) { 142 MarkerType markerType = type.getAnnotation(MarkerType.class); 143 if (markerType != null) { 144 return markerType.value(); 145 } else { 146 throw JVMCIError.shouldNotReachHere(String.format("%s extends StructuralInput, but is not annotated with @MarkerType", type)); 147 } 148 } else { 149 return InputType.Value; 150 } 151 } 152 153 private boolean processNodeIntrinsic(GraphBuilderContext b, ResolvedJavaMethod method, NodeIntrinsic intrinsic, List<ValueNode> args, Kind returnKind, Stamp stamp) { 154 ValueNode res = createNodeIntrinsic(b, method, intrinsic, args, stamp); 155 if (res == null) { 156 return false; 157 } 158 if (res instanceof UnsafeCopyNode) { 159 UnsafeCopyNode copy = (UnsafeCopyNode) res; 160 UnsafeLoadNode value = b.add(new UnsafeLoadNode(copy.sourceObject(), copy.sourceOffset(), copy.accessKind(), copy.getLocationIdentity())); 161 b.add(new UnsafeStoreNode(copy.destinationObject(), copy.destinationOffset(), value, copy.accessKind(), copy.getLocationIdentity())); 162 return true; 163 } else if (res instanceof ForeignCallNode) { 164 /* 165 * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the 166 * case that the foreign call can deoptimize. As with all deoptimization, we need a 167 * state in a non-intrinsic method. 168 */ 169 GraphBuilderContext nonIntrinsicAncestor = b.getNonIntrinsicAncestor(); 170 if (nonIntrinsicAncestor != null) { 171 ForeignCallNode foreign = (ForeignCallNode) res; 172 foreign.setBci(nonIntrinsicAncestor.bci()); 173 } 174 } 175 176 boolean nonValueType = false; 177 if (returnKind == Kind.Object && stamp instanceof ObjectStamp) { 178 ResolvedJavaType type = ((ObjectStamp) stamp).type(); 179 if (type != null && structuralInputType.isAssignableFrom(type)) { 180 assert res.isAllowedUsageType(getInputType(type)); 181 nonValueType = true; 182 } 183 } 184 185 if (returnKind != Kind.Void) { 186 assert nonValueType || res.getKind().getStackKind() != Kind.Void; 187 res = b.addPush(returnKind, res); 188 } else { 189 assert res.getKind().getStackKind() == Kind.Void; 190 res = b.add(res); 191 } 192 193 return true; 194 } 195 196 private ValueNode createNodeIntrinsic(GraphBuilderContext b, ResolvedJavaMethod method, NodeIntrinsic intrinsic, List<ValueNode> args, Stamp stamp) { 197 ValueNode res = nodeIntrinsification.createIntrinsicNode(args, stamp, method, b.getGraph(), intrinsic); 198 assert res != null : String.format("Could not create node intrinsic for call to %s as one of the arguments expected to be constant isn't: arguments=%s", method.format("%H.%n(%p)"), args); 199 return res; 200 } 201}