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         switch (operation.opcode()) {
 272             case NODE_CLASS:
 273                 assert args.length == 2;
 274                 ValueNode left = args[0];
 275                 ValueNode right = operation.rightOperandIsInt() ? toUnsigned(b, args[1], JavaKind.Int) : fromSigned(b, args[1]);
 276 
 277                 b.addPush(returnKind, createBinaryNodeInstance(operation.node(), left, right));
 278                 break;
 279 
 280             case COMPARISON:
 281                 assert args.length == 2;
 282                 b.push(returnKind, comparisonOp(b, operation.condition(), args[0], fromSigned(b, args[1])));
 283                 break;
 284 
 285             case IS_NULL:
 286                 assert args.length == 1;
 287                 b.push(returnKind, comparisonOp(b, Condition.EQ, args[0], ConstantNode.forIntegerKind(wordKind, 0L)));
 288                 break;
 289 
 290             case IS_NON_NULL:
 291                 assert args.length == 1;
 292                 b.push(returnKind, comparisonOp(b, Condition.NE, args[0], ConstantNode.forIntegerKind(wordKind, 0L)));
 293                 break;
 294 
 295             case NOT:
 296                 assert args.length == 1;
 297                 b.addPush(returnKind, new XorNode(args[0], b.add(forIntegerKind(wordKind, -1))));
 298                 break;
 299 
 300             case READ_POINTER:
 301             case READ_OBJECT:
 302             case READ_BARRIERED: {
 303                 assert args.length == 2 || args.length == 3;
 304                 JavaKind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass()));
 305                 AddressNode address = makeAddress(b, args[0], args[1]);
 306                 LocationIdentity location;
 307                 if (args.length == 2) {
 308                     location = any();
 309                 } else {
 310                     assert args[2].isConstant();
 311                     location = snippetReflection.asObject(LocationIdentity.class, args[2].asJavaConstant());
 312                 }
 313                 b.push(returnKind, readOp(b, readKind, address, location, operation.opcode()));
 314                 break;
 315             }
 316             case READ_HEAP: {
 317                 assert args.length == 3;
 318                 JavaKind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass()));
 319                 AddressNode address = makeAddress(b, args[0], args[1]);
 320                 BarrierType barrierType = snippetReflection.asObject(BarrierType.class, args[2].asJavaConstant());
 321                 b.push(returnKind, readOp(b, readKind, address, any(), barrierType, true));
 322                 break;
 323             }
 324             case WRITE_POINTER:
 325             case WRITE_OBJECT:
 326             case WRITE_BARRIERED:
 327             case INITIALIZE: {
 328                 assert args.length == 3 || args.length == 4;
 329                 JavaKind writeKind = wordTypes.asKind(wordMethod.getSignature().getParameterType(wordMethod.isStatic() ? 2 : 1, wordMethod.getDeclaringClass()));
 330                 AddressNode address = makeAddress(b, args[0], args[1]);
 331                 LocationIdentity location;
 332                 if (args.length == 3) {
 333                     location = any();
 334                 } else {
 335                     assert args[3].isConstant();
 336                     location = snippetReflection.asObject(LocationIdentity.class, args[3].asJavaConstant());
 337                 }
 338                 writeOp(b, writeKind, address, location, args[2], operation.opcode());
 339                 break;
 340             }
 341 
 342             case TO_RAW_VALUE:
 343                 assert args.length == 1;
 344                 b.push(returnKind, toUnsigned(b, args[0], JavaKind.Long));
 345                 break;
 346 
 347             case OBJECT_TO_TRACKED:
 348                 assert args.length == 1;
 349                 WordCastNode objectToTracked = b.add(WordCastNode.objectToTrackedPointer(args[0], wordKind));
 350                 b.push(returnKind, objectToTracked);
 351                 break;
 352 
 353             case OBJECT_TO_UNTRACKED:
 354                 assert args.length == 1;
 355                 WordCastNode objectToUntracked = b.add(WordCastNode.objectToUntrackedPointer(args[0], wordKind));
 356                 b.push(returnKind, objectToUntracked);
 357                 break;
 358 
 359             case FROM_ADDRESS:
 360                 assert args.length == 1;
 361                 WordCastNode addressToWord = b.add(WordCastNode.addressToWord(args[0], wordKind));
 362                 b.push(returnKind, addressToWord);
 363                 break;
 364 
 365             case TO_OBJECT:
 366                 assert args.length == 1;
 367                 WordCastNode wordToObject = b.add(WordCastNode.wordToObject(args[0], wordKind));
 368                 b.push(returnKind, wordToObject);
 369                 break;
 370 
 371             case TO_OBJECT_NON_NULL:
 372                 assert args.length == 1;
 373                 WordCastNode wordToObjectNonNull = b.add(WordCastNode.wordToObjectNonNull(args[0], wordKind));
 374                 b.push(returnKind, wordToObjectNonNull);
 375                 break;
 376 
 377             case CAS_POINTER:
 378                 assert args.length == 5;
 379                 AddressNode address = makeAddress(b, args[0], args[1]);
 380                 JavaKind valueKind = wordTypes.asKind(wordMethod.getSignature().getParameterType(1, wordMethod.getDeclaringClass()));
 381                 assert valueKind.equals(wordTypes.asKind(wordMethod.getSignature().getParameterType(2, wordMethod.getDeclaringClass()))) : wordMethod.getSignature();
 382                 assert args[4].isConstant() : Arrays.toString(args);
 383                 LocationIdentity location = snippetReflection.asObject(LocationIdentity.class, args[4].asJavaConstant());
 384                 JavaType returnType = wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass());
 385                 b.addPush(returnKind, casOp(valueKind, wordTypes.asKind(returnType), address, location, args[2], args[3]));
 386                 break;
 387             default:
 388                 throw new GraalError("Unknown opcode: %s", operation.opcode());
 389         }
 390     }
 391 
 392     /**
 393      * Create an instance of a binary node which is used to lower {@link Word} operations. This
 394      * method is called for all {@link Word} operations which are annotated with @Operation(node =
 395      * ...) and encapsulates the reflective allocation of the node.
 396      */
 397     private static ValueNode createBinaryNodeInstance(Class<? extends ValueNode> nodeClass, ValueNode left, ValueNode right) {
 398         try {
 399             Constructor<?> cons = nodeClass.getDeclaredConstructor(ValueNode.class, ValueNode.class);
 400             return (ValueNode) cons.newInstance(left, right);
 401         } catch (Throwable ex) {
 402             throw new GraalError(ex).addContext(nodeClass.getName());
 403         }
 404     }
 405 
 406     private ValueNode comparisonOp(GraphBuilderContext graph, Condition condition, ValueNode left, ValueNode right) {
 407         assert left.getStackKind() == wordKind && right.getStackKind() == wordKind;
 408 
 409         // mirroring gets the condition into canonical form
 410         boolean mirror = condition.canonicalMirror();
 411 
 412         ValueNode a = mirror ? right : left;
 413         ValueNode b = mirror ? left : right;
 414 
 415         CompareNode comparison;
 416         if (condition == Condition.EQ || condition == Condition.NE) {
 417             comparison = new IntegerEqualsNode(a, b);
 418         } else if (condition.isUnsigned()) {
 419             comparison = new IntegerBelowNode(a, b);
 420         } else {
 421             comparison = new IntegerLessThanNode(a, b);
 422         }
 423 
 424         ConstantNode trueValue = graph.add(forInt(1));
 425         ConstantNode falseValue = graph.add(forInt(0));
 426 
 427         if (condition.canonicalNegate()) {
 428             ConstantNode temp = trueValue;
 429             trueValue = falseValue;
 430             falseValue = temp;
 431         }
 432         ConditionalNode materialize = graph.add(new ConditionalNode(graph.add(comparison), trueValue, falseValue));
 433         return materialize;
 434     }
 435 
 436     protected ValueNode readOp(GraphBuilderContext b, JavaKind readKind, AddressNode address, LocationIdentity location, Opcode op) {
 437         assert op == Opcode.READ_POINTER || op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED;
 438         final BarrierType barrier = (op == Opcode.READ_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE);
 439         final boolean compressible = (op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED);
 440 
 441         return readOp(b, readKind, address, location, barrier, compressible);
 442     }
 443 
 444     public static ValueNode readOp(GraphBuilderContext b, JavaKind readKind, AddressNode address, LocationIdentity location, BarrierType barrierType, boolean compressible) {
 445         /*
 446          * A JavaReadNode lowered to a ReadNode that will not float. This means it cannot float
 447          * above an explicit zero check on its base address or any other test that ensures the read
 448          * is safe.
 449          */
 450         JavaReadNode read = b.add(new JavaReadNode(readKind, address, location, barrierType, compressible));
 451         return read;
 452     }
 453 
 454     protected void writeOp(GraphBuilderContext b, JavaKind writeKind, AddressNode address, LocationIdentity location, ValueNode value, Opcode op) {
 455         assert op == Opcode.WRITE_POINTER || op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED || op == Opcode.INITIALIZE;
 456         final BarrierType barrier = (op == Opcode.WRITE_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE);
 457         final boolean compressible = (op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED);
 458         assert op != Opcode.INITIALIZE || location.isInit() : "must use init location for initializing";
 459         b.add(new JavaWriteNode(writeKind, address, location, value, barrier, compressible));
 460     }
 461 
 462     protected AbstractCompareAndSwapNode casOp(JavaKind writeKind, JavaKind returnKind, AddressNode address, LocationIdentity location, ValueNode expectedValue, ValueNode newValue) {
 463         boolean isLogic = returnKind == JavaKind.Boolean;
 464         assert isLogic || writeKind == returnKind : writeKind + " != " + returnKind;
 465         AbstractCompareAndSwapNode cas;
 466         if (isLogic) {
 467             cas = new LogicCompareAndSwapNode(address, expectedValue, newValue, location);
 468         } else {
 469             cas = new ValueCompareAndSwapNode(address, expectedValue, newValue, location);
 470         }
 471         return cas;
 472     }
 473 
 474     public AddressNode makeAddress(GraphBuilderContext b, ValueNode base, ValueNode offset) {
 475         return b.add(new OffsetAddressNode(base, fromSigned(b, offset)));
 476     }
 477 
 478     public ValueNode fromUnsigned(GraphBuilderContext b, ValueNode value) {
 479         return convert(b, value, wordKind, true);
 480     }
 481 
 482     public ValueNode fromSigned(GraphBuilderContext b, ValueNode value) {
 483         return convert(b, value, wordKind, false);
 484     }
 485 
 486     public ValueNode toUnsigned(GraphBuilderContext b, ValueNode value, JavaKind toKind) {
 487         return convert(b, value, toKind, true);
 488     }
 489 
 490     public ValueNode convert(GraphBuilderContext b, ValueNode value, JavaKind toKind, boolean unsigned) {
 491         if (value.getStackKind() == toKind) {
 492             return value;
 493         }
 494 
 495         if (toKind == JavaKind.Int) {
 496             assert value.getStackKind() == JavaKind.Long;
 497             return b.add(new NarrowNode(value, 32));
 498         } else {
 499             assert toKind == JavaKind.Long;
 500             assert value.getStackKind() == JavaKind.Int;
 501             if (unsigned) {
 502                 return b.add(new ZeroExtendNode(value, 64));
 503             } else {
 504                 return b.add(new SignExtendNode(value, 64));
 505             }
 506         }
 507     }
 508 
 509     public WordTypes getWordTypes() {
 510         return wordTypes;
 511     }
 512 
 513     private static BailoutException bailout(GraphBuilderContext b, String msg) {
 514         throw b.bailout(msg + "\nat " + b.getCode().asStackTraceElement(b.bci()));
 515     }
 516 }