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