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