1 /* 2 * Copyright (c) 2015, 2016, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.replacements; 24 25 import static org.graalvm.compiler.core.common.LocationIdentity.any; 26 import static org.graalvm.compiler.nodes.ConstantNode.forInt; 27 import static org.graalvm.compiler.nodes.ConstantNode.forIntegerKind; 28 29 import java.lang.reflect.Constructor; 30 31 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 32 import org.graalvm.compiler.bytecode.BridgeMethodUtils; 33 import org.graalvm.compiler.core.common.LocationIdentity; 34 import org.graalvm.compiler.core.common.calc.Condition; 35 import org.graalvm.compiler.core.common.type.Stamp; 36 import org.graalvm.compiler.core.common.type.StampFactory; 37 import org.graalvm.compiler.core.common.type.StampPair; 38 import org.graalvm.compiler.core.common.type.TypeReference; 39 import org.graalvm.compiler.debug.GraalError; 40 import org.graalvm.compiler.nodes.ConstantNode; 41 import org.graalvm.compiler.nodes.Invoke; 42 import org.graalvm.compiler.nodes.ValueNode; 43 import org.graalvm.compiler.nodes.calc.CompareNode; 44 import org.graalvm.compiler.nodes.calc.ConditionalNode; 45 import org.graalvm.compiler.nodes.calc.IntegerBelowNode; 46 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; 47 import org.graalvm.compiler.nodes.calc.IntegerLessThanNode; 48 import org.graalvm.compiler.nodes.calc.NarrowNode; 49 import org.graalvm.compiler.nodes.calc.SignExtendNode; 50 import org.graalvm.compiler.nodes.calc.XorNode; 51 import org.graalvm.compiler.nodes.calc.ZeroExtendNode; 52 import org.graalvm.compiler.nodes.extended.JavaReadNode; 53 import org.graalvm.compiler.nodes.extended.JavaWriteNode; 54 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; 55 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderTool; 56 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; 57 import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin; 58 import org.graalvm.compiler.nodes.graphbuilderconf.TypePlugin; 59 import org.graalvm.compiler.nodes.java.LoadFieldNode; 60 import org.graalvm.compiler.nodes.java.LoadIndexedNode; 61 import org.graalvm.compiler.nodes.java.StoreIndexedNode; 62 import org.graalvm.compiler.nodes.memory.HeapAccess.BarrierType; 63 import org.graalvm.compiler.nodes.memory.address.AddressNode; 64 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; 65 import org.graalvm.compiler.nodes.type.StampTool; 66 import org.graalvm.compiler.word.Word; 67 import org.graalvm.compiler.word.Word.Opcode; 68 import org.graalvm.compiler.word.Word.Operation; 69 import org.graalvm.compiler.word.WordTypes; 70 import org.graalvm.compiler.word.nodes.WordCastNode; 71 72 import jdk.vm.ci.code.BailoutException; 73 import jdk.vm.ci.meta.JavaKind; 74 import jdk.vm.ci.meta.JavaType; 75 import jdk.vm.ci.meta.JavaTypeProfile; 76 import jdk.vm.ci.meta.ResolvedJavaField; 77 import jdk.vm.ci.meta.ResolvedJavaMethod; 78 import jdk.vm.ci.meta.ResolvedJavaType; 79 80 /** 81 * A plugin for calls to {@linkplain Operation word operations}, as well as all other nodes that 82 * need special handling for {@link Word} types. 83 */ 84 public class WordOperationPlugin implements NodePlugin, TypePlugin, InlineInvokePlugin { 85 protected final WordTypes wordTypes; 86 protected final JavaKind wordKind; 87 protected final SnippetReflectionProvider snippetReflection; 88 89 public WordOperationPlugin(SnippetReflectionProvider snippetReflection, WordTypes wordTypes) { 90 this.snippetReflection = snippetReflection; 91 this.wordTypes = wordTypes; 92 this.wordKind = wordTypes.getWordKind(); 93 } 94 95 @Override 96 public boolean canChangeStackKind(GraphBuilderContext b) { 97 return true; 98 } 99 100 /** 101 * Processes a call to a method if it is annotated with {@link Operation} by adding nodes to the 102 * graph being built that implement the denoted operation. 103 * 104 * @return {@code true} iff {@code method} is annotated with {@link Operation} (and was thus 105 * processed by this method) 106 */ 107 @Override 108 public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 109 if (!wordTypes.isWordOperation(method)) { 110 return false; 111 } 112 processWordOperation(b, args, wordTypes.getWordOperation(method, b.getMethod().getDeclaringClass())); 113 return true; 114 } 115 116 @Override 117 public StampPair interceptType(GraphBuilderTool b, JavaType declaredType, boolean nonNull) { 118 Stamp wordStamp = null; 119 if (declaredType instanceof ResolvedJavaType) { 120 ResolvedJavaType resolved = (ResolvedJavaType) declaredType; 121 if (wordTypes.isWord(resolved)) { 122 wordStamp = wordTypes.getWordStamp(resolved); 123 } else if (resolved.isArray() && wordTypes.isWord(resolved.getElementalType())) { 124 TypeReference trusted = TypeReference.createTrustedWithoutAssumptions(resolved); 125 wordStamp = StampFactory.object(trusted, nonNull); 126 } 127 } 128 if (wordStamp != null) { 129 return StampPair.createSingle(wordStamp); 130 } else { 131 return null; 132 } 133 } 134 135 @Override 136 public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { 137 if (wordTypes.isWord(invoke.asNode())) { 138 invoke.asNode().setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(invoke.asNode()))); 139 } 140 } 141 142 @Override 143 public boolean handleLoadField(GraphBuilderContext b, ValueNode receiver, ResolvedJavaField field) { 144 StampPair wordStamp = interceptType(b, field.getType(), false); 145 if (wordStamp != null) { 146 LoadFieldNode loadFieldNode = LoadFieldNode.createOverrideStamp(wordStamp, receiver, field); 147 b.addPush(field.getJavaKind(), loadFieldNode); 148 return true; 149 } 150 return false; 151 } 152 153 @Override 154 public boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField staticField) { 155 return handleLoadField(b, null, staticField); 156 } 157 158 @Override 159 public boolean handleLoadIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, JavaKind elementKind) { 160 ResolvedJavaType arrayType = StampTool.typeOrNull(array); 161 /* 162 * There are cases where the array does not have a known type yet, i.e., the type is null. 163 * In that case we assume it is not a word type. 164 */ 165 if (arrayType != null && wordTypes.isWord(arrayType.getComponentType())) { 166 assert elementKind == JavaKind.Object; 167 b.addPush(elementKind, createLoadIndexedNode(array, index)); 168 return true; 169 } 170 return false; 171 } 172 173 protected LoadIndexedNode createLoadIndexedNode(ValueNode array, ValueNode index) { 174 return new LoadIndexedNode(null, array, index, wordTypes.getWordKind()); 175 } 176 177 @Override 178 public boolean handleStoreField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field, ValueNode value) { 179 if (field.getJavaKind() == JavaKind.Object) { 180 boolean isWordField = wordTypes.isWord(field.getType()); 181 boolean isWordValue = value.getStackKind() == wordTypes.getWordKind(); 182 183 if (isWordField && !isWordValue) { 184 throw bailout(b, "Cannot store a non-word value into a word field: " + field.format("%H.%n")); 185 } else if (!isWordField && isWordValue) { 186 throw bailout(b, "Cannot store a word value into a non-word field: " + field.format("%H.%n")); 187 } 188 } 189 190 /* We never need to intercept the field store. */ 191 return false; 192 } 193 194 @Override 195 public boolean handleStoreStaticField(GraphBuilderContext b, ResolvedJavaField field, ValueNode value) { 196 return handleStoreField(b, null, field, value); 197 } 198 199 @Override 200 public boolean handleStoreIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, JavaKind elementKind, ValueNode value) { 201 ResolvedJavaType arrayType = StampTool.typeOrNull(array); 202 if (arrayType != null && wordTypes.isWord(arrayType.getComponentType())) { 203 assert elementKind == JavaKind.Object; 204 if (value.getStackKind() != wordTypes.getWordKind()) { 205 throw bailout(b, "Cannot store a non-word value into a word array: " + arrayType.toJavaName(true)); 206 } 207 b.add(createStoreIndexedNode(array, index, value)); 208 return true; 209 } 210 if (elementKind == JavaKind.Object && value.getStackKind() == wordTypes.getWordKind()) { 211 throw bailout(b, "Cannot store a word value into a non-word array: " + arrayType.toJavaName(true)); 212 } 213 return false; 214 } 215 216 protected StoreIndexedNode createStoreIndexedNode(ValueNode array, ValueNode index, ValueNode value) { 217 return new StoreIndexedNode(array, index, wordTypes.getWordKind(), value); 218 } 219 220 @Override 221 public boolean handleCheckCast(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { 222 if (!wordTypes.isWord(type)) { 223 if (object.getStackKind() != JavaKind.Object) { 224 throw bailout(b, "Cannot cast a word value to a non-word type: " + type.toJavaName(true)); 225 } 226 return false; 227 } 228 229 if (object.getStackKind() != wordTypes.getWordKind()) { 230 throw bailout(b, "Cannot cast a non-word value to a word type: " + type.toJavaName(true)); 231 } 232 b.push(JavaKind.Object, object); 233 return true; 234 } 235 236 @Override 237 public boolean handleInstanceOf(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { 238 if (wordTypes.isWord(type)) { 239 throw bailout(b, "Cannot use instanceof for word a type: " + type.toJavaName(true)); 240 } else if (object.getStackKind() != JavaKind.Object) { 241 throw bailout(b, "Cannot use instanceof on a word value: " + type.toJavaName(true)); 242 } 243 return false; 244 } 245 246 protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, ResolvedJavaMethod wordMethod) throws GraalError { 247 Operation operation = BridgeMethodUtils.getAnnotation(Word.Operation.class, wordMethod); 248 JavaKind returnKind = wordMethod.getSignature().getReturnKind(); 249 switch (operation.opcode()) { 250 case NODE_CLASS: 251 assert args.length == 2; 252 ValueNode left = args[0]; 253 ValueNode right = operation.rightOperandIsInt() ? toUnsigned(b, args[1], JavaKind.Int) : fromSigned(b, args[1]); 254 255 b.addPush(returnKind, createBinaryNodeInstance(operation.node(), left, right)); 256 break; 257 258 case COMPARISON: 259 assert args.length == 2; 260 b.push(returnKind, comparisonOp(b, operation.condition(), args[0], fromSigned(b, args[1]))); 261 break; 262 263 case NOT: 264 assert args.length == 1; 265 b.addPush(returnKind, new XorNode(args[0], b.add(forIntegerKind(wordKind, -1)))); 266 break; 267 268 case READ_POINTER: 269 case READ_OBJECT: 270 case READ_BARRIERED: { 271 assert args.length == 2 || args.length == 3; 272 JavaKind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass())); 273 AddressNode address = makeAddress(b, args[0], args[1]); 274 LocationIdentity location; 275 if (args.length == 2) { 276 location = any(); 277 } else { 278 assert args[2].isConstant(); 279 location = snippetReflection.asObject(LocationIdentity.class, args[2].asJavaConstant()); 280 } 281 b.push(returnKind, readOp(b, readKind, address, location, operation.opcode())); 282 break; 283 } 284 case READ_HEAP: { 285 assert args.length == 3; 286 JavaKind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass())); 287 AddressNode address = makeAddress(b, args[0], args[1]); 288 BarrierType barrierType = snippetReflection.asObject(BarrierType.class, args[2].asJavaConstant()); 289 b.push(returnKind, readOp(b, readKind, address, any(), barrierType, true)); 290 break; 291 } 292 case WRITE_POINTER: 293 case WRITE_OBJECT: 294 case WRITE_BARRIERED: 295 case INITIALIZE: { 296 assert args.length == 3 || args.length == 4; 297 JavaKind writeKind = wordTypes.asKind(wordMethod.getSignature().getParameterType(wordMethod.isStatic() ? 2 : 1, wordMethod.getDeclaringClass())); 298 AddressNode address = makeAddress(b, args[0], args[1]); 299 LocationIdentity location; 300 if (args.length == 3) { 301 location = any(); 302 } else { 303 assert args[3].isConstant(); 304 location = snippetReflection.asObject(LocationIdentity.class, args[3].asJavaConstant()); 305 } 306 writeOp(b, writeKind, address, location, args[2], operation.opcode()); 307 break; 308 } 309 case ZERO: 310 assert args.length == 0; 311 b.addPush(returnKind, forIntegerKind(wordKind, 0L)); 312 break; 313 314 case FROM_UNSIGNED: 315 assert args.length == 1; 316 b.push(returnKind, fromUnsigned(b, args[0])); 317 break; 318 319 case FROM_SIGNED: 320 assert args.length == 1; 321 b.push(returnKind, fromSigned(b, args[0])); 322 break; 323 324 case TO_RAW_VALUE: 325 assert args.length == 1; 326 b.push(returnKind, toUnsigned(b, args[0], JavaKind.Long)); 327 break; 328 329 case OBJECT_TO_TRACKED: 330 assert args.length == 1; 331 WordCastNode objectToTracked = b.add(WordCastNode.objectToTrackedPointer(args[0], wordKind)); 332 b.push(returnKind, objectToTracked); 333 break; 334 335 case OBJECT_TO_UNTRACKED: 336 assert args.length == 1; 337 WordCastNode objectToUntracked = b.add(WordCastNode.objectToUntrackedPointer(args[0], wordKind)); 338 b.push(returnKind, objectToUntracked); 339 break; 340 341 case FROM_ADDRESS: 342 assert args.length == 1; 343 WordCastNode addressToWord = b.add(WordCastNode.addressToWord(args[0], wordKind)); 344 b.push(returnKind, addressToWord); 345 break; 346 347 case TO_OBJECT: 348 assert args.length == 1; 349 WordCastNode wordToObject = b.add(WordCastNode.wordToObject(args[0], wordKind)); 350 b.push(returnKind, wordToObject); 351 break; 352 353 default: 354 throw new GraalError("Unknown opcode: %s", operation.opcode()); 355 } 356 } 357 358 /** 359 * Create an instance of a binary node which is used to lower {@link Word} operations. This 360 * method is called for all {@link Word} operations which are annotated with @Operation(node = 361 * ...) and encapsulates the reflective allocation of the node. 362 */ 363 private static ValueNode createBinaryNodeInstance(Class<? extends ValueNode> nodeClass, ValueNode left, ValueNode right) { 364 try { 365 Constructor<?> cons = nodeClass.getDeclaredConstructor(ValueNode.class, ValueNode.class); 366 return (ValueNode) cons.newInstance(left, right); 367 } catch (Throwable ex) { 368 throw new GraalError(ex).addContext(nodeClass.getName()); 369 } 370 } 371 372 private ValueNode comparisonOp(GraphBuilderContext graph, Condition condition, ValueNode left, ValueNode right) { 373 assert left.getStackKind() == wordKind && right.getStackKind() == wordKind; 374 375 // mirroring gets the condition into canonical form 376 boolean mirror = condition.canonicalMirror(); 377 378 ValueNode a = mirror ? right : left; 379 ValueNode b = mirror ? left : right; 380 381 CompareNode comparison; 382 if (condition == Condition.EQ || condition == Condition.NE) { 383 comparison = new IntegerEqualsNode(a, b); 384 } else if (condition.isUnsigned()) { 385 comparison = new IntegerBelowNode(a, b); 386 } else { 387 comparison = new IntegerLessThanNode(a, b); 388 } 389 390 ConstantNode trueValue = graph.add(forInt(1)); 391 ConstantNode falseValue = graph.add(forInt(0)); 392 393 if (condition.canonicalNegate()) { 394 ConstantNode temp = trueValue; 395 trueValue = falseValue; 396 falseValue = temp; 397 } 398 ConditionalNode materialize = graph.add(new ConditionalNode(graph.add(comparison), trueValue, falseValue)); 399 return materialize; 400 } 401 402 protected ValueNode readOp(GraphBuilderContext b, JavaKind readKind, AddressNode address, LocationIdentity location, Opcode op) { 403 assert op == Opcode.READ_POINTER || op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED; 404 final BarrierType barrier = (op == Opcode.READ_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE); 405 final boolean compressible = (op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED); 406 407 return readOp(b, readKind, address, location, barrier, compressible); 408 } 409 410 public static ValueNode readOp(GraphBuilderContext b, JavaKind readKind, AddressNode address, LocationIdentity location, BarrierType barrierType, boolean compressible) { 411 /* 412 * A JavaReadNode lowered to a ReadNode that will not float. This means it cannot float 413 * above an explicit zero check on its base address or any other test that ensures the read 414 * is safe. 415 */ 416 JavaReadNode read = b.add(new JavaReadNode(readKind, address, location, barrierType, compressible)); 417 return read; 418 } 419 420 protected void writeOp(GraphBuilderContext b, JavaKind writeKind, AddressNode address, LocationIdentity location, ValueNode value, Opcode op) { 421 assert op == Opcode.WRITE_POINTER || op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED || op == Opcode.INITIALIZE; 422 final BarrierType barrier = (op == Opcode.WRITE_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE); 423 final boolean compressible = (op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED); 424 final boolean initialize = (op == Opcode.INITIALIZE); 425 b.add(new JavaWriteNode(writeKind, address, location, value, barrier, compressible, initialize)); 426 } 427 428 public AddressNode makeAddress(GraphBuilderContext b, ValueNode base, ValueNode offset) { 429 return b.add(new OffsetAddressNode(base, fromSigned(b, offset))); 430 } 431 432 public ValueNode fromUnsigned(GraphBuilderContext b, ValueNode value) { 433 return convert(b, value, wordKind, true); 434 } 435 436 public ValueNode fromSigned(GraphBuilderContext b, ValueNode value) { 437 return convert(b, value, wordKind, false); 438 } 439 440 public ValueNode toUnsigned(GraphBuilderContext b, ValueNode value, JavaKind toKind) { 441 return convert(b, value, toKind, true); 442 } 443 444 public ValueNode convert(GraphBuilderContext b, ValueNode value, JavaKind toKind, boolean unsigned) { 445 if (value.getStackKind() == toKind) { 446 return value; 447 } 448 449 if (toKind == JavaKind.Int) { 450 assert value.getStackKind() == JavaKind.Long; 451 return b.add(new NarrowNode(value, 32)); 452 } else { 453 assert toKind == JavaKind.Long; 454 assert value.getStackKind() == JavaKind.Int; 455 if (unsigned) { 456 return b.add(new ZeroExtendNode(value, 64)); 457 } else { 458 return b.add(new SignExtendNode(value, 64)); 459 } 460 } 461 } 462 463 public WordTypes getWordTypes() { 464 return wordTypes; 465 } 466 467 private static BailoutException bailout(GraphBuilderContext b, String msg) { 468 throw b.bailout(msg + "\nat " + b.getCode().asStackTraceElement(b.bci())); 469 } 470 }