001/* 002 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.replacements; 024 025import static com.oracle.graal.nodes.ConstantNode.*; 026import static jdk.internal.jvmci.meta.LocationIdentity.*; 027 028import java.lang.reflect.*; 029 030import jdk.internal.jvmci.common.*; 031import jdk.internal.jvmci.meta.*; 032 033import com.oracle.graal.api.replacements.*; 034import com.oracle.graal.compiler.common.calc.*; 035import com.oracle.graal.compiler.common.type.*; 036import com.oracle.graal.graphbuilderconf.*; 037import com.oracle.graal.nodes.*; 038import com.oracle.graal.nodes.calc.*; 039import com.oracle.graal.nodes.extended.*; 040import com.oracle.graal.nodes.java.*; 041import com.oracle.graal.nodes.memory.HeapAccess.BarrierType; 042import com.oracle.graal.nodes.memory.address.*; 043import com.oracle.graal.nodes.type.*; 044import com.oracle.graal.word.*; 045import com.oracle.graal.word.Word.Opcode; 046import com.oracle.graal.word.Word.Operation; 047import com.oracle.graal.word.nodes.*; 048 049/** 050 * A plugin for calls to {@linkplain Operation word operations}, as well as all other nodes that 051 * need special handling for {@link Word} types. 052 */ 053public class WordOperationPlugin implements NodePlugin, ParameterPlugin, InlineInvokePlugin { 054 protected final WordTypes wordTypes; 055 protected final Kind wordKind; 056 protected final SnippetReflectionProvider snippetReflection; 057 058 public WordOperationPlugin(SnippetReflectionProvider snippetReflection, WordTypes wordTypes) { 059 this.snippetReflection = snippetReflection; 060 this.wordTypes = wordTypes; 061 this.wordKind = wordTypes.getWordKind(); 062 } 063 064 @Override 065 public boolean canChangeStackKind(GraphBuilderContext b) { 066 return true; 067 } 068 069 /** 070 * Processes a call to a method if it is annotated with {@link Operation} by adding nodes to the 071 * graph being built that implement the denoted operation. 072 * 073 * @return {@code true} iff {@code method} is annotated with {@link Operation} (and was thus 074 * processed by this method) 075 */ 076 @Override 077 public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 078 if (!wordTypes.isWordOperation(method)) { 079 return false; 080 } 081 processWordOperation(b, args, wordTypes.getWordOperation(method, b.getMethod().getDeclaringClass())); 082 return true; 083 } 084 085 @Override 086 public FloatingNode interceptParameter(GraphBuilderContext b, int index, Stamp stamp) { 087 ResolvedJavaType type = StampTool.typeOrNull(stamp); 088 if (wordTypes.isWord(type)) { 089 return new ParameterNode(index, wordTypes.getWordStamp(type)); 090 } 091 return null; 092 } 093 094 @Override 095 public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { 096 if (wordTypes.isWord(invoke.asNode())) { 097 invoke.asNode().setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(invoke.asNode()))); 098 } 099 } 100 101 @Override 102 public boolean handleLoadField(GraphBuilderContext b, ValueNode receiver, ResolvedJavaField field) { 103 if (field.getType() instanceof ResolvedJavaType && wordTypes.isWord((ResolvedJavaType) field.getType())) { 104 LoadFieldNode loadFieldNode = new LoadFieldNode(receiver, field); 105 loadFieldNode.setStamp(wordTypes.getWordStamp((ResolvedJavaType) field.getType())); 106 b.addPush(field.getKind(), loadFieldNode); 107 return true; 108 } 109 return false; 110 } 111 112 @Override 113 public boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField staticField) { 114 return handleLoadField(b, null, staticField); 115 } 116 117 @Override 118 public boolean handleLoadIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, Kind elementKind) { 119 ResolvedJavaType arrayType = StampTool.typeOrNull(array); 120 /* 121 * There are cases where the array does not have a known type yet, i.e., the type is null. 122 * In that case we assume it is not a word type. 123 */ 124 if (arrayType != null && wordTypes.isWord(arrayType.getComponentType())) { 125 assert elementKind == Kind.Object; 126 b.addPush(elementKind, createLoadIndexedNode(array, index)); 127 return true; 128 } 129 return false; 130 } 131 132 protected LoadIndexedNode createLoadIndexedNode(ValueNode array, ValueNode index) { 133 return new LoadIndexedNode(array, index, wordTypes.getWordKind()); 134 } 135 136 @Override 137 public boolean handleStoreIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, Kind elementKind, ValueNode value) { 138 ResolvedJavaType arrayType = StampTool.typeOrNull(array); 139 if (arrayType != null && wordTypes.isWord(arrayType.getComponentType())) { 140 assert elementKind == Kind.Object; 141 if (value.getKind() != wordTypes.getWordKind()) { 142 throw b.bailout("Cannot store a non-word value into a word array: " + arrayType.toJavaName(true)); 143 } 144 b.add(createStoreIndexedNode(array, index, value)); 145 return true; 146 } 147 if (elementKind == Kind.Object && value.getKind() == wordTypes.getWordKind()) { 148 throw b.bailout("Cannot store a word value into a non-word array: " + arrayType.toJavaName(true)); 149 } 150 return false; 151 } 152 153 protected StoreIndexedNode createStoreIndexedNode(ValueNode array, ValueNode index, ValueNode value) { 154 return new StoreIndexedNode(array, index, wordTypes.getWordKind(), value); 155 } 156 157 @Override 158 public boolean handleCheckCast(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { 159 if (!wordTypes.isWord(type)) { 160 if (object.getKind() != Kind.Object) { 161 throw b.bailout("Cannot cast a word value to a non-word type: " + type.toJavaName(true)); 162 } 163 return false; 164 } 165 166 if (object.getKind() != wordTypes.getWordKind()) { 167 throw b.bailout("Cannot cast a non-word value to a word type: " + type.toJavaName(true)); 168 } 169 b.push(Kind.Object, object); 170 return true; 171 } 172 173 @Override 174 public boolean handleInstanceOf(GraphBuilderContext b, ValueNode object, ResolvedJavaType type, JavaTypeProfile profile) { 175 if (wordTypes.isWord(type)) { 176 throw b.bailout("Cannot use instanceof for word a type: " + type.toJavaName(true)); 177 } else if (object.getKind() != Kind.Object) { 178 throw b.bailout("Cannot use instanceof on a word value: " + type.toJavaName(true)); 179 } 180 return false; 181 } 182 183 protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, ResolvedJavaMethod wordMethod) throws JVMCIError { 184 Operation operation = wordMethod.getAnnotation(Word.Operation.class); 185 Kind returnKind = wordMethod.getSignature().getReturnKind(); 186 switch (operation.opcode()) { 187 case NODE_CLASS: 188 assert args.length == 2; 189 ValueNode left = args[0]; 190 ValueNode right = operation.rightOperandIsInt() ? toUnsigned(b, args[1], Kind.Int) : fromSigned(b, args[1]); 191 192 b.addPush(returnKind, createBinaryNodeInstance(operation.node(), left, right)); 193 break; 194 195 case COMPARISON: 196 assert args.length == 2; 197 b.push(returnKind, comparisonOp(b, operation.condition(), args[0], fromSigned(b, args[1]))); 198 break; 199 200 case NOT: 201 assert args.length == 1; 202 b.addPush(returnKind, new XorNode(args[0], b.add(forIntegerKind(wordKind, -1)))); 203 break; 204 205 case READ_POINTER: 206 case READ_OBJECT: 207 case READ_BARRIERED: { 208 assert args.length == 2 || args.length == 3; 209 Kind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass())); 210 AddressNode address = makeAddress(b, args[0], args[1]); 211 LocationIdentity location; 212 if (args.length == 2) { 213 location = any(); 214 } else { 215 assert args[2].isConstant(); 216 location = snippetReflection.asObject(LocationIdentity.class, args[2].asJavaConstant()); 217 } 218 b.push(returnKind, readOp(b, readKind, address, location, operation.opcode())); 219 break; 220 } 221 case READ_HEAP: { 222 assert args.length == 3; 223 Kind readKind = wordTypes.asKind(wordMethod.getSignature().getReturnType(wordMethod.getDeclaringClass())); 224 AddressNode address = makeAddress(b, args[0], args[1]); 225 BarrierType barrierType = snippetReflection.asObject(BarrierType.class, args[2].asJavaConstant()); 226 b.push(returnKind, readOp(b, readKind, address, any(), barrierType, true)); 227 break; 228 } 229 case WRITE_POINTER: 230 case WRITE_OBJECT: 231 case WRITE_BARRIERED: 232 case INITIALIZE: { 233 assert args.length == 3 || args.length == 4; 234 Kind writeKind = wordTypes.asKind(wordMethod.getSignature().getParameterType(wordMethod.isStatic() ? 2 : 1, wordMethod.getDeclaringClass())); 235 AddressNode address = makeAddress(b, args[0], args[1]); 236 LocationIdentity location; 237 if (args.length == 3) { 238 location = any(); 239 } else { 240 assert args[3].isConstant(); 241 location = snippetReflection.asObject(LocationIdentity.class, args[3].asJavaConstant()); 242 } 243 writeOp(b, writeKind, address, location, args[2], operation.opcode()); 244 break; 245 } 246 case ZERO: 247 assert args.length == 0; 248 b.addPush(returnKind, forIntegerKind(wordKind, 0L)); 249 break; 250 251 case FROM_UNSIGNED: 252 assert args.length == 1; 253 b.push(returnKind, fromUnsigned(b, args[0])); 254 break; 255 256 case FROM_SIGNED: 257 assert args.length == 1; 258 b.push(returnKind, fromSigned(b, args[0])); 259 break; 260 261 case TO_RAW_VALUE: 262 assert args.length == 1; 263 b.push(returnKind, toUnsigned(b, args[0], Kind.Long)); 264 break; 265 266 case FROM_WORDBASE: 267 assert args.length == 1; 268 b.push(returnKind, args[0]); 269 break; 270 271 case FROM_OBJECT: 272 assert args.length == 1; 273 WordCastNode objectToWord = b.add(WordCastNode.objectToWord(args[0], wordKind)); 274 b.push(returnKind, objectToWord); 275 break; 276 277 case FROM_ADDRESS: 278 assert args.length == 1; 279 WordCastNode addressToWord = b.add(WordCastNode.addressToWord(args[0], wordKind)); 280 b.push(returnKind, addressToWord); 281 break; 282 283 case TO_OBJECT: 284 assert args.length == 1; 285 WordCastNode wordToObject = b.add(WordCastNode.wordToObject(args[0], wordKind)); 286 b.push(returnKind, wordToObject); 287 break; 288 289 default: 290 throw new JVMCIError("Unknown opcode: %s", operation.opcode()); 291 } 292 } 293 294 /** 295 * Create an instance of a binary node which is used to lower {@link Word} operations. This 296 * method is called for all {@link Word} operations which are annotated with @Operation(node = 297 * ...) and encapsulates the reflective allocation of the node. 298 */ 299 private static ValueNode createBinaryNodeInstance(Class<? extends ValueNode> nodeClass, ValueNode left, ValueNode right) { 300 try { 301 Constructor<?> cons = nodeClass.getDeclaredConstructor(ValueNode.class, ValueNode.class); 302 return (ValueNode) cons.newInstance(left, right); 303 } catch (Throwable ex) { 304 throw new JVMCIError(ex).addContext(nodeClass.getName()); 305 } 306 } 307 308 private ValueNode comparisonOp(GraphBuilderContext graph, Condition condition, ValueNode left, ValueNode right) { 309 assert left.getKind() == wordKind && right.getKind() == wordKind; 310 311 // mirroring gets the condition into canonical form 312 boolean mirror = condition.canonicalMirror(); 313 314 ValueNode a = mirror ? right : left; 315 ValueNode b = mirror ? left : right; 316 317 CompareNode comparison; 318 if (condition == Condition.EQ || condition == Condition.NE) { 319 comparison = new IntegerEqualsNode(a, b); 320 } else if (condition.isUnsigned()) { 321 comparison = new IntegerBelowNode(a, b); 322 } else { 323 comparison = new IntegerLessThanNode(a, b); 324 } 325 326 ConstantNode trueValue = graph.add(forInt(1)); 327 ConstantNode falseValue = graph.add(forInt(0)); 328 329 if (condition.canonicalNegate()) { 330 ConstantNode temp = trueValue; 331 trueValue = falseValue; 332 falseValue = temp; 333 } 334 ConditionalNode materialize = graph.add(new ConditionalNode(graph.add(comparison), trueValue, falseValue)); 335 return materialize; 336 } 337 338 protected ValueNode readOp(GraphBuilderContext b, Kind readKind, AddressNode address, LocationIdentity location, Opcode op) { 339 assert op == Opcode.READ_POINTER || op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED; 340 final BarrierType barrier = (op == Opcode.READ_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE); 341 final boolean compressible = (op == Opcode.READ_OBJECT || op == Opcode.READ_BARRIERED); 342 343 return readOp(b, readKind, address, location, barrier, compressible); 344 } 345 346 public static ValueNode readOp(GraphBuilderContext b, Kind readKind, AddressNode address, LocationIdentity location, BarrierType barrierType, boolean compressible) { 347 /* 348 * A JavaReadNode lowered to a ReadNode that will not float. This means it cannot float 349 * above an explicit zero check on its base address or any other test that ensures the read 350 * is safe. 351 */ 352 JavaReadNode read = b.add(new JavaReadNode(readKind, address, location, barrierType, compressible)); 353 return read; 354 } 355 356 protected void writeOp(GraphBuilderContext b, Kind writeKind, AddressNode address, LocationIdentity location, ValueNode value, Opcode op) { 357 assert op == Opcode.WRITE_POINTER || op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED || op == Opcode.INITIALIZE; 358 final BarrierType barrier = (op == Opcode.WRITE_BARRIERED ? BarrierType.PRECISE : BarrierType.NONE); 359 final boolean compressible = (op == Opcode.WRITE_OBJECT || op == Opcode.WRITE_BARRIERED); 360 final boolean initialize = (op == Opcode.INITIALIZE); 361 b.add(new JavaWriteNode(writeKind, address, location, value, barrier, compressible, initialize)); 362 } 363 364 public AddressNode makeAddress(GraphBuilderContext b, ValueNode base, ValueNode offset) { 365 return b.add(new OffsetAddressNode(base, fromSigned(b, offset))); 366 } 367 368 public ValueNode fromUnsigned(GraphBuilderContext b, ValueNode value) { 369 return convert(b, value, wordKind, true); 370 } 371 372 public ValueNode fromSigned(GraphBuilderContext b, ValueNode value) { 373 return convert(b, value, wordKind, false); 374 } 375 376 public ValueNode toUnsigned(GraphBuilderContext b, ValueNode value, Kind toKind) { 377 return convert(b, value, toKind, true); 378 } 379 380 public ValueNode convert(GraphBuilderContext b, ValueNode value, Kind toKind, boolean unsigned) { 381 if (value.getKind() == toKind) { 382 return value; 383 } 384 385 if (toKind == Kind.Int) { 386 assert value.getKind() == Kind.Long; 387 return b.add(new NarrowNode(value, 32)); 388 } else { 389 assert toKind == Kind.Long; 390 assert value.getKind().getStackKind() == Kind.Int; 391 if (unsigned) { 392 return b.add(new ZeroExtendNode(value, 64)); 393 } else { 394 return b.add(new SignExtendNode(value, 64)); 395 } 396 } 397 } 398 399 public WordTypes getWordTypes() { 400 return wordTypes; 401 } 402}