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