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 }