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.truffle.substitutions; 024 025import static java.lang.Character.*; 026 027import java.util.concurrent.*; 028 029import jdk.internal.jvmci.meta.*; 030 031import com.oracle.graal.api.replacements.*; 032import com.oracle.graal.compiler.common.calc.*; 033import com.oracle.graal.compiler.common.type.*; 034import com.oracle.graal.graph.*; 035import com.oracle.graal.graphbuilderconf.*; 036import com.oracle.graal.graphbuilderconf.InvocationPlugin.Receiver; 037import com.oracle.graal.graphbuilderconf.InvocationPlugins.Registration; 038import com.oracle.graal.nodes.*; 039import com.oracle.graal.nodes.calc.*; 040import com.oracle.graal.nodes.extended.*; 041import com.oracle.graal.replacements.nodes.arithmetic.*; 042import com.oracle.graal.truffle.*; 043import com.oracle.graal.truffle.nodes.*; 044import com.oracle.graal.truffle.nodes.asserts.*; 045import com.oracle.graal.truffle.nodes.frame.*; 046import com.oracle.graal.truffle.unsafe.*; 047import com.oracle.truffle.api.*; 048import com.oracle.truffle.api.frame.*; 049 050/** 051 * Provides {@link InvocationPlugin}s for Truffle classes. 052 */ 053public class TruffleGraphBuilderPlugins { 054 public static void registerInvocationPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins, boolean canDelayIntrinsification, SnippetReflectionProvider snippetReflection) { 055 056 registerOptimizedAssumptionPlugins(plugins, snippetReflection); 057 registerExactMathPlugins(plugins); 058 registerCompilerDirectivesPlugins(plugins); 059 registerCompilerAssertsPlugins(plugins, canDelayIntrinsification); 060 registerOptimizedCallTargetPlugins(metaAccess, plugins); 061 registerUnsafeAccessImplPlugins(plugins, canDelayIntrinsification); 062 063 if (TruffleCompilerOptions.TruffleUseFrameWithoutBoxing.getValue()) { 064 registerFrameWithoutBoxingPlugins(plugins, canDelayIntrinsification); 065 } else { 066 registerFrameWithBoxingPlugins(plugins, canDelayIntrinsification); 067 } 068 069 } 070 071 public static void registerOptimizedAssumptionPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection) { 072 Registration r = new Registration(plugins, OptimizedAssumption.class); 073 InvocationPlugin plugin = new InvocationPlugin() { 074 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 075 if (receiver.isConstant()) { 076 Constant constant = receiver.get().asConstant(); 077 OptimizedAssumption assumption = snippetReflection.asObject(OptimizedAssumption.class, (JavaConstant) constant); 078 if (assumption.isValid()) { 079 if (targetMethod.getName().equals("isValid")) { 080 b.addPush(Kind.Boolean, ConstantNode.forBoolean(true)); 081 } else { 082 assert targetMethod.getName().equals("check") : targetMethod; 083 } 084 b.getAssumptions().record(new AssumptionValidAssumption(assumption)); 085 } else { 086 if (targetMethod.getName().equals("isValid")) { 087 b.addPush(Kind.Boolean, ConstantNode.forBoolean(false)); 088 } else { 089 assert targetMethod.getName().equals("check") : targetMethod; 090 b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.None)); 091 } 092 } 093 return true; 094 } else { 095 return false; 096 } 097 } 098 }; 099 r.register1("isValid", Receiver.class, plugin); 100 r.register1("check", Receiver.class, plugin); 101 } 102 103 public static void registerExactMathPlugins(InvocationPlugins plugins) { 104 Registration r = new Registration(plugins, ExactMath.class); 105 for (Kind kind : new Kind[]{Kind.Int, Kind.Long}) { 106 Class<?> type = kind.toJavaClass(); 107 r.register2("addExact", type, type, new InvocationPlugin() { 108 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) { 109 b.addPush(kind, new IntegerAddExactNode(x, y)); 110 return true; 111 } 112 }); 113 r.register2("subtractExact", type, type, new InvocationPlugin() { 114 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) { 115 b.addPush(kind, new IntegerSubExactNode(x, y)); 116 return true; 117 } 118 }); 119 r.register2("multiplyExact", type, type, new InvocationPlugin() { 120 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) { 121 b.addPush(kind, new IntegerMulExactNode(x, y)); 122 return true; 123 } 124 }); 125 r.register2("multiplyHigh", type, type, new InvocationPlugin() { 126 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) { 127 b.addPush(kind, new IntegerMulHighNode(x, y)); 128 return true; 129 } 130 }); 131 r.register2("multiplyHighUnsigned", type, type, new InvocationPlugin() { 132 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x, ValueNode y) { 133 b.addPush(kind, new UnsignedMulHighNode(x, y)); 134 return true; 135 } 136 }); 137 } 138 } 139 140 public static void registerCompilerDirectivesPlugins(InvocationPlugins plugins) { 141 Registration r = new Registration(plugins, CompilerDirectives.class); 142 r.register0("inInterpreter", new InvocationPlugin() { 143 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 144 b.addPush(Kind.Boolean, ConstantNode.forBoolean(false)); 145 return true; 146 } 147 }); 148 r.register0("inCompiledCode", new InvocationPlugin() { 149 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 150 b.addPush(Kind.Boolean, ConstantNode.forBoolean(true)); 151 return true; 152 } 153 }); 154 r.register0("transferToInterpreter", new InvocationPlugin() { 155 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 156 b.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.TransferToInterpreter)); 157 return true; 158 } 159 }); 160 r.register0("transferToInterpreterAndInvalidate", new InvocationPlugin() { 161 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 162 b.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TransferToInterpreter)); 163 return true; 164 } 165 }); 166 r.register1("interpreterOnly", Runnable.class, new InvocationPlugin() { 167 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) { 168 return true; 169 } 170 }); 171 r.register1("interpreterOnly", Callable.class, new InvocationPlugin() { 172 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) { 173 return true; 174 } 175 }); 176 r.register2("injectBranchProbability", double.class, boolean.class, new InvocationPlugin() { 177 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode probability, ValueNode condition) { 178 b.addPush(Kind.Boolean, new BranchProbabilityNode(probability, condition)); 179 return true; 180 } 181 }); 182 r.register1("bailout", String.class, new InvocationPlugin() { 183 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) { 184 if (message.isConstant()) { 185 throw b.bailout(message.asConstant().toValueString()); 186 } 187 throw b.bailout("bailout (message is not compile-time constant, so no additional information is available)"); 188 } 189 }); 190 r.register1("isCompilationConstant", Object.class, new InvocationPlugin() { 191 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { 192 if ((value instanceof BoxNode ? ((BoxNode) value).getValue() : value).isConstant()) { 193 b.addPush(Kind.Boolean, ConstantNode.forBoolean(true)); 194 } else { 195 b.addPush(Kind.Boolean, new IsCompilationConstantNode(value)); 196 } 197 return true; 198 } 199 }); 200 r.register1("materialize", Object.class, new InvocationPlugin() { 201 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { 202 b.add(new ForceMaterializeNode(value)); 203 return true; 204 } 205 }); 206 } 207 208 public static void registerCompilerAssertsPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) { 209 Registration r = new Registration(plugins, CompilerAsserts.class); 210 r.register1("partialEvaluationConstant", Object.class, new InvocationPlugin() { 211 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { 212 ValueNode curValue = value; 213 if (curValue instanceof BoxNode) { 214 BoxNode boxNode = (BoxNode) curValue; 215 curValue = boxNode.getValue(); 216 } 217 if (curValue.isConstant()) { 218 return true; 219 } else if (canDelayIntrinsification) { 220 return false; 221 } else { 222 StringBuilder sb = new StringBuilder(); 223 sb.append(curValue); 224 if (curValue instanceof ValuePhiNode) { 225 ValuePhiNode valuePhi = (ValuePhiNode) curValue; 226 sb.append(" ("); 227 for (Node n : valuePhi.inputs()) { 228 sb.append(n); 229 sb.append("; "); 230 } 231 sb.append(")"); 232 } 233 throw b.bailout("Partial evaluation did not reduce value to a constant, is a regular compiler node: " + sb.toString()); 234 } 235 } 236 }); 237 r.register0("neverPartOfCompilation", new InvocationPlugin() { 238 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { 239 b.add(new NeverPartOfCompilationNode("CompilerAsserts.neverPartOfCompilation()")); 240 return true; 241 } 242 }); 243 r.register1("neverPartOfCompilation", String.class, new InvocationPlugin() { 244 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) { 245 if (message.isConstant()) { 246 String messageString = message.asConstant().toValueString(); 247 b.add(new NeverPartOfCompilationNode(messageString)); 248 return true; 249 } else { 250 throw b.bailout("message for never part of compilation is non-constant"); 251 } 252 } 253 }); 254 } 255 256 public static void registerOptimizedCallTargetPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) { 257 Registration r = new Registration(plugins, OptimizedCallTarget.class); 258 r.register2("createFrame", FrameDescriptor.class, Object[].class, new InvocationPlugin() { 259 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode descriptor, ValueNode args) { 260 Class<?> frameClass = TruffleCompilerOptions.TruffleUseFrameWithoutBoxing.getValue() ? FrameWithoutBoxing.class : FrameWithBoxing.class; 261 b.addPush(Kind.Object, new NewFrameNode(StampFactory.exactNonNull(metaAccess.lookupJavaType(frameClass)), descriptor, args)); 262 return true; 263 } 264 }); 265 r.register2("castArrayFixedLength", Object[].class, int.class, new InvocationPlugin() { 266 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode args, ValueNode length) { 267 b.addPush(Kind.Object, new PiArrayNode(args, length, args.stamp())); 268 return true; 269 } 270 }); 271 } 272 273 public static void registerFrameWithoutBoxingPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) { 274 Registration r = new Registration(plugins, FrameWithoutBoxing.class); 275 registerMaterialize(r); 276 registerUnsafeCast(r, canDelayIntrinsification); 277 registerUnsafeLoadStorePlugins(r, Kind.Int, Kind.Long, Kind.Float, Kind.Double, Kind.Object); 278 } 279 280 public static void registerFrameWithBoxingPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) { 281 Registration r = new Registration(plugins, FrameWithBoxing.class); 282 registerMaterialize(r); 283 registerUnsafeCast(r, canDelayIntrinsification); 284 } 285 286 public static void registerUnsafeAccessImplPlugins(InvocationPlugins plugins, boolean canDelayIntrinsification) { 287 Registration r = new Registration(plugins, UnsafeAccessImpl.class); 288 registerUnsafeCast(r, canDelayIntrinsification); 289 registerUnsafeLoadStorePlugins(r, Kind.Boolean, Kind.Byte, Kind.Int, Kind.Short, Kind.Long, Kind.Float, Kind.Double, Kind.Object); 290 } 291 292 private static void registerMaterialize(Registration r) { 293 r.register1("materialize", Receiver.class, new InvocationPlugin() { 294 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver frame) { 295 b.addPush(Kind.Object, new MaterializeFrameNode(frame.get())); 296 return true; 297 } 298 }); 299 } 300 301 private static void registerUnsafeCast(Registration r, boolean canDelayIntrinsification) { 302 r.register4("unsafeCast", Object.class, Class.class, boolean.class, boolean.class, new InvocationPlugin() { 303 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode clazz, ValueNode condition, ValueNode nonNull) { 304 if (clazz.isConstant() && nonNull.isConstant()) { 305 ConstantReflectionProvider constantReflection = b.getConstantReflection(); 306 ResolvedJavaType javaType = constantReflection.asJavaType(clazz.asConstant()); 307 if (javaType == null) { 308 b.push(Kind.Object, object); 309 } else { 310 Stamp piStamp = null; 311 if (javaType.isArray()) { 312 if (nonNull.asJavaConstant().asInt() != 0) { 313 piStamp = StampFactory.exactNonNull(javaType); 314 } else { 315 piStamp = StampFactory.exact(javaType); 316 } 317 } else { 318 piStamp = StampFactory.declaredTrusted(javaType, nonNull.asJavaConstant().asInt() != 0); 319 } 320 LogicNode compareNode = CompareNode.createCompareNode(object.graph(), Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), constantReflection); 321 boolean skipAnchor = false; 322 if (compareNode instanceof LogicConstantNode) { 323 LogicConstantNode logicConstantNode = (LogicConstantNode) compareNode; 324 if (logicConstantNode.getValue()) { 325 skipAnchor = true; 326 } 327 } 328 ConditionAnchorNode valueAnchorNode = null; 329 if (!skipAnchor) { 330 valueAnchorNode = b.add(new ConditionAnchorNode(compareNode)); 331 } 332 b.addPush(Kind.Object, new PiNode(object, piStamp, valueAnchorNode)); 333 } 334 return true; 335 } else if (canDelayIntrinsification) { 336 return false; 337 } else { 338 throw b.bailout("unsafeCast arguments could not reduce to a constant: " + clazz + ", " + nonNull); 339 } 340 } 341 }); 342 } 343 344 protected static void registerUnsafeLoadStorePlugins(Registration r, Kind... kinds) { 345 for (Kind kind : kinds) { 346 String kindName = kind.getJavaName(); 347 kindName = toUpperCase(kindName.charAt(0)) + kindName.substring(1); 348 String getName = "unsafeGet" + kindName; 349 String putName = "unsafePut" + kindName; 350 r.register4(getName, Object.class, long.class, boolean.class, Object.class, new CustomizedUnsafeLoadPlugin(kind)); 351 r.register4(putName, Object.class, long.class, kind == Kind.Object ? Object.class : kind.toJavaClass(), Object.class, new CustomizedUnsafeStorePlugin(kind)); 352 } 353 } 354 355 static class CustomizedUnsafeLoadPlugin implements InvocationPlugin { 356 357 private final Kind returnKind; 358 359 public CustomizedUnsafeLoadPlugin(Kind returnKind) { 360 this.returnKind = returnKind; 361 } 362 363 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode condition, ValueNode location) { 364 if (location.isConstant()) { 365 LocationIdentity locationIdentity; 366 if (location.isNullConstant()) { 367 locationIdentity = LocationIdentity.any(); 368 } else { 369 locationIdentity = ObjectLocationIdentity.create(location.asJavaConstant()); 370 } 371 LogicNode compare = b.add(CompareNode.createCompareNode(Condition.EQ, condition, ConstantNode.forBoolean(true, object.graph()), b.getConstantReflection())); 372 b.addPush(returnKind, b.add(new UnsafeLoadNode(object, offset, returnKind, locationIdentity, compare))); 373 return true; 374 } 375 // TODO: should we throw b.bailout() here? 376 return false; 377 } 378 } 379 380 static class CustomizedUnsafeStorePlugin implements InvocationPlugin { 381 382 private final Kind kind; 383 384 public CustomizedUnsafeStorePlugin(Kind kind) { 385 this.kind = kind; 386 } 387 388 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object, ValueNode offset, ValueNode value, ValueNode location) { 389 ValueNode locationArgument = location; 390 if (locationArgument.isConstant()) { 391 LocationIdentity locationIdentity; 392 if (locationArgument.isNullConstant()) { 393 locationIdentity = LocationIdentity.any(); 394 } else { 395 locationIdentity = ObjectLocationIdentity.create(locationArgument.asJavaConstant()); 396 } 397 398 b.add(new UnsafeStoreNode(object, offset, value, kind, locationIdentity, null)); 399 return true; 400 } 401 // TODO: should we throw b.bailout() here? 402 return false; 403 } 404 } 405}