1 /* 2 * Copyright (c) 2013, 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.core.aarch64; 24 25 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant; 26 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant; 27 28 import java.util.function.Function; 29 30 import org.graalvm.compiler.asm.aarch64.AArch64Address.AddressingMode; 31 import org.graalvm.compiler.asm.aarch64.AArch64Assembler.ConditionFlag; 32 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler; 33 import org.graalvm.compiler.core.common.LIRKind; 34 import org.graalvm.compiler.core.common.calc.Condition; 35 import org.graalvm.compiler.core.common.spi.LIRKindTool; 36 import org.graalvm.compiler.debug.GraalError; 37 import org.graalvm.compiler.lir.LIRFrameState; 38 import org.graalvm.compiler.lir.LIRValueUtil; 39 import org.graalvm.compiler.lir.LabelRef; 40 import org.graalvm.compiler.lir.StandardOp; 41 import org.graalvm.compiler.lir.SwitchStrategy; 42 import org.graalvm.compiler.lir.Variable; 43 import org.graalvm.compiler.lir.aarch64.AArch64AddressValue; 44 import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp; 45 import org.graalvm.compiler.lir.aarch64.AArch64ArrayCompareToOp; 46 import org.graalvm.compiler.lir.aarch64.AArch64ArrayEqualsOp; 47 import org.graalvm.compiler.lir.aarch64.AArch64ByteSwapOp; 48 import org.graalvm.compiler.lir.aarch64.AArch64Compare; 49 import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow; 50 import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow.BranchOp; 51 import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow.CondMoveOp; 52 import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow.StrategySwitchOp; 53 import org.graalvm.compiler.lir.aarch64.AArch64ControlFlow.TableSwitchOp; 54 import org.graalvm.compiler.lir.aarch64.AArch64Move; 55 import org.graalvm.compiler.lir.aarch64.AArch64AtomicMove.AtomicReadAndAddOp; 56 import org.graalvm.compiler.lir.aarch64.AArch64AtomicMove.CompareAndSwapOp; 57 import org.graalvm.compiler.lir.aarch64.AArch64Move.MembarOp; 58 import org.graalvm.compiler.lir.aarch64.AArch64PauseOp; 59 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 60 import org.graalvm.compiler.lir.gen.LIRGenerator; 61 import org.graalvm.compiler.phases.util.Providers; 62 63 import jdk.vm.ci.aarch64.AArch64; 64 import jdk.vm.ci.aarch64.AArch64Kind; 65 import jdk.vm.ci.code.CallingConvention; 66 import jdk.vm.ci.code.RegisterValue; 67 import jdk.vm.ci.meta.AllocatableValue; 68 import jdk.vm.ci.meta.JavaConstant; 69 import jdk.vm.ci.meta.JavaKind; 70 import jdk.vm.ci.meta.PlatformKind; 71 import jdk.vm.ci.meta.PrimitiveConstant; 72 import jdk.vm.ci.meta.Value; 73 import jdk.vm.ci.meta.ValueKind; 74 75 public abstract class AArch64LIRGenerator extends LIRGenerator { 76 77 public AArch64LIRGenerator(LIRKindTool lirKindTool, AArch64ArithmeticLIRGenerator arithmeticLIRGen, MoveFactory moveFactory, Providers providers, LIRGenerationResult lirGenRes) { 78 super(lirKindTool, arithmeticLIRGen, moveFactory, providers, lirGenRes); 79 } 80 81 /** 82 * Checks whether the supplied constant can be used without loading it into a register for store 83 * operations, i.e., on the right hand side of a memory access. 84 * 85 * @param c The constant to check. 86 * @return True if the constant can be used directly, false if the constant needs to be in a 87 * register. 88 */ 89 protected static final boolean canStoreConstant(JavaConstant c) { 90 // Our own code never calls this since we can't make a definite statement about whether or 91 // not we can inline a constant without knowing what kind of operation we execute. Let's be 92 // optimistic here and fix up mistakes later. 93 return true; 94 } 95 96 /** 97 * AArch64 cannot use anything smaller than a word in any instruction other than load and store. 98 */ 99 @Override 100 public <K extends ValueKind<K>> K toRegisterKind(K kind) { 101 switch ((AArch64Kind) kind.getPlatformKind()) { 102 case BYTE: 103 case WORD: 104 return kind.changeType(AArch64Kind.DWORD); 105 default: 106 return kind; 107 } 108 } 109 110 @Override 111 public void emitNullCheck(Value address, LIRFrameState state) { 112 append(new AArch64Move.NullCheckOp(asAddressValue(address), state)); 113 } 114 115 @Override 116 public Variable emitAddress(AllocatableValue stackslot) { 117 Variable result = newVariable(LIRKind.value(target().arch.getWordKind())); 118 append(new AArch64Move.StackLoadAddressOp(result, stackslot)); 119 return result; 120 } 121 122 public AArch64AddressValue asAddressValue(Value address) { 123 if (address instanceof AArch64AddressValue) { 124 return (AArch64AddressValue) address; 125 } else { 126 return new AArch64AddressValue(address.getValueKind(), asAllocatable(address), Value.ILLEGAL, 0, 1, AddressingMode.BASE_REGISTER_ONLY); 127 } 128 } 129 130 @Override 131 public Variable emitLogicCompareAndSwap(LIRKind accessKind, Value address, Value expectedValue, Value newValue, Value trueValue, Value falseValue) { 132 Variable prevValue = newVariable(expectedValue.getValueKind()); 133 Variable scratch = newVariable(LIRKind.value(AArch64Kind.DWORD)); 134 append(new CompareAndSwapOp(prevValue, loadReg(expectedValue), loadReg(newValue), asAllocatable(address), scratch)); 135 assert trueValue.getValueKind().equals(falseValue.getValueKind()); 136 Variable result = newVariable(trueValue.getValueKind()); 137 append(new CondMoveOp(result, ConditionFlag.EQ, asAllocatable(trueValue), asAllocatable(falseValue))); 138 return result; 139 } 140 141 @Override 142 public Variable emitValueCompareAndSwap(LIRKind accessKind, Value address, Value expectedValue, Value newValue) { 143 Variable result = newVariable(newValue.getValueKind()); 144 Variable scratch = newVariable(LIRKind.value(AArch64Kind.WORD)); 145 append(new CompareAndSwapOp(result, loadNonCompareConst(expectedValue), loadReg(newValue), asAllocatable(address), scratch)); 146 return result; 147 } 148 149 @Override 150 public Value emitAtomicReadAndAdd(Value address, ValueKind<?> kind, Value delta) { 151 Variable result = newVariable(kind); 152 Variable scratch1 = newVariable(kind); 153 Variable scratch2 = newVariable(kind); 154 155 append(new AtomicReadAndAddOp((AArch64Kind) kind.getPlatformKind(), asAllocatable(result), asAllocatable(address), delta, asAllocatable(scratch1), asAllocatable(scratch2))); 156 return result; 157 } 158 159 @Override 160 public void emitMembar(int barriers) { 161 int necessaryBarriers = target().arch.requiredBarriers(barriers); 162 if (target().isMP && necessaryBarriers != 0) { 163 append(new MembarOp(necessaryBarriers)); 164 } 165 } 166 167 @Override 168 public void emitJump(LabelRef label) { 169 assert label != null; 170 append(new StandardOp.JumpOp(label)); 171 } 172 173 @Override 174 public void emitOverflowCheckBranch(LabelRef overflow, LabelRef noOverflow, LIRKind cmpKind, double overflowProbability) { 175 append(new AArch64ControlFlow.BranchOp(ConditionFlag.VS, overflow, noOverflow, overflowProbability)); 176 } 177 178 /** 179 * Branches to label if (left & right) == 0. If negated is true branchse on non-zero instead. 180 * 181 * @param left Integer kind. Non null. 182 * @param right Integer kind. Non null. 183 * @param trueDestination destination if left & right == 0. Non null. 184 * @param falseDestination destination if left & right != 0. Non null 185 * @param trueSuccessorProbability hoistoric probability that comparison is true 186 */ 187 @Override 188 public void emitIntegerTestBranch(Value left, Value right, LabelRef trueDestination, LabelRef falseDestination, double trueSuccessorProbability) { 189 assert ((AArch64Kind) left.getPlatformKind()).isInteger() && left.getPlatformKind() == right.getPlatformKind(); 190 ((AArch64ArithmeticLIRGenerator) getArithmetic()).emitBinary(LIRKind.combine(left, right), AArch64ArithmeticOp.ANDS, true, left, right); 191 append(new AArch64ControlFlow.BranchOp(ConditionFlag.EQ, trueDestination, falseDestination, trueSuccessorProbability)); 192 } 193 194 /** 195 * Conditionally move trueValue into new variable if cond + unorderedIsTrue is true, else 196 * falseValue. 197 * 198 * @param left Arbitrary value. Has to have same type as right. Non null. 199 * @param right Arbitrary value. Has to have same type as left. Non null. 200 * @param cond condition that decides whether to move trueValue or falseValue into result. Non 201 * null. 202 * @param unorderedIsTrue defines whether floating-point comparisons consider unordered true or 203 * not. Ignored for integer comparisons. 204 * @param trueValue arbitrary value same type as falseValue. Non null. 205 * @param falseValue arbitrary value same type as trueValue. Non null. 206 * @return value containing trueValue if cond + unorderedIsTrue is true, else falseValue. Non 207 * null. 208 */ 209 @Override 210 public Variable emitConditionalMove(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, Value trueValue, Value falseValue) { 211 boolean mirrored = emitCompare(cmpKind, left, right, cond, unorderedIsTrue); 212 Condition finalCondition = mirrored ? cond.mirror() : cond; 213 boolean finalUnorderedIsTrue = mirrored ? !unorderedIsTrue : unorderedIsTrue; 214 ConditionFlag cmpCondition = toConditionFlag(((AArch64Kind) cmpKind).isInteger(), finalCondition, finalUnorderedIsTrue); 215 Variable result = newVariable(trueValue.getValueKind()); 216 append(new CondMoveOp(result, cmpCondition, loadReg(trueValue), loadReg(falseValue))); 217 return result; 218 } 219 220 @Override 221 public void emitCompareBranch(PlatformKind cmpKind, Value left, Value right, Condition cond, boolean unorderedIsTrue, LabelRef trueDestination, LabelRef falseDestination, 222 double trueDestinationProbability) { 223 boolean mirrored = emitCompare(cmpKind, left, right, cond, unorderedIsTrue); 224 Condition finalCondition = mirrored ? cond.mirror() : cond; 225 boolean finalUnorderedIsTrue = mirrored ? !unorderedIsTrue : unorderedIsTrue; 226 ConditionFlag cmpCondition = toConditionFlag(((AArch64Kind) cmpKind).isInteger(), finalCondition, finalUnorderedIsTrue); 227 append(new BranchOp(cmpCondition, trueDestination, falseDestination, trueDestinationProbability)); 228 } 229 230 private static ConditionFlag toConditionFlag(boolean isInt, Condition cond, boolean unorderedIsTrue) { 231 return isInt ? toIntConditionFlag(cond) : toFloatConditionFlag(cond, unorderedIsTrue); 232 } 233 234 /** 235 * Takes a Condition and unorderedIsTrue flag and returns the correct Aarch64 specific 236 * ConditionFlag. Note: This is only correct if the emitCompare code for floats has correctly 237 * handled the case of 'EQ && unorderedIsTrue', respectively 'NE && !unorderedIsTrue'! 238 */ 239 private static ConditionFlag toFloatConditionFlag(Condition cond, boolean unorderedIsTrue) { 240 switch (cond) { 241 case LT: 242 return unorderedIsTrue ? ConditionFlag.LT : ConditionFlag.LO; 243 case LE: 244 return unorderedIsTrue ? ConditionFlag.LE : ConditionFlag.LS; 245 case GE: 246 return unorderedIsTrue ? ConditionFlag.PL : ConditionFlag.GE; 247 case GT: 248 return unorderedIsTrue ? ConditionFlag.HI : ConditionFlag.GT; 249 case EQ: 250 return ConditionFlag.EQ; 251 case NE: 252 return ConditionFlag.NE; 253 default: 254 throw GraalError.shouldNotReachHere(); 255 } 256 } 257 258 /** 259 * Takes a Condition and returns the correct Aarch64 specific ConditionFlag. 260 */ 261 private static ConditionFlag toIntConditionFlag(Condition cond) { 262 switch (cond) { 263 case EQ: 264 return ConditionFlag.EQ; 265 case NE: 266 return ConditionFlag.NE; 267 case LT: 268 return ConditionFlag.LT; 269 case LE: 270 return ConditionFlag.LE; 271 case GT: 272 return ConditionFlag.GT; 273 case GE: 274 return ConditionFlag.GE; 275 case AE: 276 return ConditionFlag.HS; 277 case BE: 278 return ConditionFlag.LS; 279 case AT: 280 return ConditionFlag.HI; 281 case BT: 282 return ConditionFlag.LO; 283 default: 284 throw GraalError.shouldNotReachHere(); 285 } 286 } 287 288 /** 289 * This method emits the compare instruction, and may reorder the operands. It returns true if 290 * it did so. 291 * 292 * @param a the left operand of the comparison. Has to have same type as b. Non null. 293 * @param b the right operand of the comparison. Has to have same type as a. Non null. 294 * @return true if mirrored (i.e. "b cmp a" instead of "a cmp b" was done). 295 */ 296 protected boolean emitCompare(PlatformKind cmpKind, Value a, Value b, Condition condition, boolean unorderedIsTrue) { 297 Value left; 298 Value right; 299 boolean mirrored; 300 AArch64Kind kind = (AArch64Kind) cmpKind; 301 if (kind.isInteger()) { 302 Value aExt = a; 303 Value bExt = b; 304 305 int compareBytes = cmpKind.getSizeInBytes(); 306 // AArch64 compares 32 or 64 bits: sign extend a and b as required. 307 if (compareBytes < a.getPlatformKind().getSizeInBytes()) { 308 aExt = arithmeticLIRGen.emitSignExtend(a, compareBytes * 8, 64); 309 } 310 if (compareBytes < b.getPlatformKind().getSizeInBytes()) { 311 bExt = arithmeticLIRGen.emitSignExtend(b, compareBytes * 8, 64); 312 } 313 314 if (LIRValueUtil.isVariable(bExt)) { 315 left = load(bExt); 316 right = loadNonConst(aExt); 317 mirrored = true; 318 } else { 319 left = load(aExt); 320 right = loadNonConst(bExt); 321 mirrored = false; 322 } 323 append(new AArch64Compare.CompareOp(left, loadNonCompareConst(right))); 324 } else if (kind.isSIMD()) { 325 if (AArch64Compare.FloatCompareOp.isFloatCmpConstant(a, condition, unorderedIsTrue)) { 326 left = load(b); 327 right = a; 328 mirrored = true; 329 } else if (AArch64Compare.FloatCompareOp.isFloatCmpConstant(b, condition, unorderedIsTrue)) { 330 left = load(a); 331 right = b; 332 mirrored = false; 333 } else { 334 left = load(a); 335 right = loadReg(b); 336 mirrored = false; 337 } 338 append(new AArch64Compare.FloatCompareOp(left, asAllocatable(right), condition, unorderedIsTrue)); 339 } else { 340 throw GraalError.shouldNotReachHere(); 341 } 342 return mirrored; 343 } 344 345 /** 346 * If value is a constant that cannot be used directly with a gpCompare instruction load it into 347 * a register and return the register, otherwise return constant value unchanged. 348 */ 349 protected Value loadNonCompareConst(Value value) { 350 if (!isCompareConstant(value)) { 351 return loadReg(value); 352 } 353 return value; 354 } 355 356 /** 357 * Checks whether value can be used directly with a gpCompare instruction. This is <b>not</b> 358 * the same as {@link AArch64ArithmeticLIRGenerator#isArithmeticConstant(JavaConstant)}, because 359 * 0.0 is a valid compare constant for floats, while there are no arithmetic constants for 360 * floats. 361 * 362 * @param value any type. Non null. 363 * @return true if value can be used directly in comparison instruction, false otherwise. 364 */ 365 public boolean isCompareConstant(Value value) { 366 if (isJavaConstant(value)) { 367 JavaConstant constant = asJavaConstant(value); 368 if (constant instanceof PrimitiveConstant) { 369 final long longValue = constant.asLong(); 370 long maskedValue; 371 switch (constant.getJavaKind()) { 372 case Boolean: 373 case Byte: 374 maskedValue = longValue & 0xFF; 375 break; 376 case Char: 377 case Short: 378 maskedValue = longValue & 0xFFFF; 379 break; 380 case Int: 381 maskedValue = longValue & 0xFFFF_FFFF; 382 break; 383 case Long: 384 maskedValue = longValue; 385 break; 386 default: 387 throw GraalError.shouldNotReachHere(); 388 } 389 return AArch64MacroAssembler.isArithmeticImmediate(maskedValue); 390 } else { 391 return constant.isDefaultForKind(); 392 } 393 } 394 return false; 395 } 396 397 /** 398 * Moves trueValue into result if (left & right) == 0, else falseValue. 399 * 400 * @param left Integer kind. Non null. 401 * @param right Integer kind. Non null. 402 * @param trueValue Integer kind. Non null. 403 * @param falseValue Integer kind. Non null. 404 * @return virtual register containing trueValue if (left & right) == 0, else falseValue. 405 */ 406 @Override 407 public Variable emitIntegerTestMove(Value left, Value right, Value trueValue, Value falseValue) { 408 assert ((AArch64Kind) left.getPlatformKind()).isInteger() && ((AArch64Kind) right.getPlatformKind()).isInteger(); 409 assert ((AArch64Kind) trueValue.getPlatformKind()).isInteger() && ((AArch64Kind) falseValue.getPlatformKind()).isInteger(); 410 ((AArch64ArithmeticLIRGenerator) getArithmetic()).emitBinary(left.getValueKind(), AArch64ArithmeticOp.ANDS, true, left, right); 411 Variable result = newVariable(trueValue.getValueKind()); 412 append(new CondMoveOp(result, ConditionFlag.EQ, load(trueValue), load(falseValue))); 413 return result; 414 } 415 416 @Override 417 public void emitStrategySwitch(SwitchStrategy strategy, Variable key, LabelRef[] keyTargets, LabelRef defaultTarget) { 418 append(createStrategySwitchOp(strategy, keyTargets, defaultTarget, key, newVariable(key.getValueKind()), AArch64LIRGenerator::toIntConditionFlag)); 419 } 420 421 protected StrategySwitchOp createStrategySwitchOp(SwitchStrategy strategy, LabelRef[] keyTargets, LabelRef defaultTarget, Variable key, AllocatableValue scratchValue, 422 Function<Condition, ConditionFlag> converter) { 423 return new StrategySwitchOp(strategy, keyTargets, defaultTarget, key, scratchValue, converter); 424 } 425 426 @Override 427 protected void emitTableSwitch(int lowKey, LabelRef defaultTarget, LabelRef[] targets, Value key) { 428 append(new TableSwitchOp(lowKey, defaultTarget, targets, key, newVariable(LIRKind.value(target().arch.getWordKind())), newVariable(key.getValueKind()))); 429 } 430 431 @Override 432 public Variable emitByteSwap(Value input) { 433 Variable result = newVariable(LIRKind.combine(input)); 434 append(new AArch64ByteSwapOp(result, input)); 435 return result; 436 } 437 438 @Override 439 public Variable emitArrayCompareTo(JavaKind kind1, JavaKind kind2, Value array1, Value array2, Value length1, Value length2) { 440 LIRKind resultKind = LIRKind.value(AArch64Kind.DWORD); 441 // DMS TODO: check calling conversion and registers used 442 RegisterValue res = AArch64.r0.asValue(resultKind); 443 RegisterValue cnt1 = AArch64.r1.asValue(length1.getValueKind()); 444 RegisterValue cnt2 = AArch64.r2.asValue(length2.getValueKind()); 445 emitMove(cnt1, length1); 446 emitMove(cnt2, length2); 447 append(new AArch64ArrayCompareToOp(this, kind1, kind2, res, array1, array2, cnt1, cnt2)); 448 Variable result = newVariable(resultKind); 449 emitMove(result, res); 450 return result; 451 } 452 453 @Override 454 public Variable emitArrayEquals(JavaKind kind, Value array1, Value array2, Value length) { 455 Variable result = newVariable(LIRKind.value(AArch64Kind.DWORD)); 456 append(new AArch64ArrayEqualsOp(this, kind, result, array1, array2, asAllocatable(length))); 457 return result; 458 } 459 460 @Override 461 protected JavaConstant zapValueForKind(PlatformKind kind) { 462 long dead = 0xDEADDEADDEADDEADL; 463 switch ((AArch64Kind) kind) { 464 case BYTE: 465 return JavaConstant.forByte((byte) dead); 466 case WORD: 467 return JavaConstant.forShort((short) dead); 468 case DWORD: 469 return JavaConstant.forInt((int) dead); 470 case QWORD: 471 return JavaConstant.forLong(dead); 472 case SINGLE: 473 return JavaConstant.forFloat(Float.intBitsToFloat((int) dead)); 474 case DOUBLE: 475 return JavaConstant.forDouble(Double.longBitsToDouble(dead)); 476 default: 477 throw GraalError.shouldNotReachHere(); 478 } 479 } 480 481 /** 482 * Loads value into virtual register. Contrary to {@link #load(Value)} this handles 483 * RegisterValues (i.e. values corresponding to fixed physical registers) correctly, by not 484 * creating an unnecessary move into a virtual register. 485 * 486 * This avoids generating the following code: mov x0, x19 # x19 is fixed thread register ldr x0, 487 * [x0] instead of: ldr x0, [x19]. 488 */ 489 protected AllocatableValue loadReg(Value val) { 490 if (!(val instanceof Variable || val instanceof RegisterValue)) { 491 return emitMove(val); 492 } 493 return (AllocatableValue) val; 494 } 495 496 @Override 497 public void emitPause() { 498 append(new AArch64PauseOp()); 499 } 500 501 public abstract void emitCCall(long address, CallingConvention nativeCallingConvention, Value[] args); 502 }