1 /*
   2  * Copyright (c) 2015, 2018, 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 
  24 
  25 
  26 package org.graalvm.compiler.core.aarch64;
  27 
  28 import static jdk.vm.ci.aarch64.AArch64Kind.DWORD;
  29 import static jdk.vm.ci.aarch64.AArch64Kind.QWORD;
  30 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant;
  31 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
  32 import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.BSR;
  33 import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.CLZ;
  34 import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.CTZ;
  35 
  36 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
  37 import org.graalvm.compiler.core.common.LIRKind;
  38 import org.graalvm.compiler.core.common.NumUtil;
  39 import org.graalvm.compiler.core.common.calc.FloatConvert;
  40 import org.graalvm.compiler.debug.GraalError;
  41 import org.graalvm.compiler.lir.ConstantValue;
  42 import org.graalvm.compiler.lir.LIRFrameState;
  43 import org.graalvm.compiler.lir.Variable;
  44 import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
  45 import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool;
  46 import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp;
  47 import org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp;
  48 import org.graalvm.compiler.lir.aarch64.AArch64Move.LoadOp;
  49 import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreConstantOp;
  50 import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreOp;
  51 import org.graalvm.compiler.lir.aarch64.AArch64ReinterpretOp;
  52 import org.graalvm.compiler.lir.aarch64.AArch64SignExtendOp;
  53 import org.graalvm.compiler.lir.aarch64.AArch64Unary;
  54 import org.graalvm.compiler.lir.gen.ArithmeticLIRGenerator;
  55 
  56 import jdk.vm.ci.aarch64.AArch64Kind;
  57 import jdk.vm.ci.meta.AllocatableValue;
  58 import jdk.vm.ci.meta.JavaConstant;
  59 import jdk.vm.ci.meta.PlatformKind;
  60 import jdk.vm.ci.meta.Value;
  61 import jdk.vm.ci.meta.ValueKind;
  62 
  63 public class AArch64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implements AArch64ArithmeticLIRGeneratorTool {
  64 
  65     @Override
  66     public AArch64LIRGenerator getLIRGen() {
  67         return (AArch64LIRGenerator) super.getLIRGen();
  68     }
  69 
  70     @Override
  71     protected boolean isNumericInteger(PlatformKind kind) {
  72         return ((AArch64Kind) kind).isInteger();
  73     }
  74 
  75     @Override
  76     protected Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags) {
  77         if (isNumericInteger(a.getPlatformKind())) {
  78             AArch64ArithmeticOp op = setFlags ? AArch64ArithmeticOp.ADDS : AArch64ArithmeticOp.ADD;
  79             return emitBinary(resultKind, op, true, a, b);
  80         } else {
  81             assert !setFlags : "Cannot set flags on floating point arithmetic";
  82             return emitBinary(resultKind, AArch64ArithmeticOp.FADD, true, a, b);
  83         }
  84     }
  85 
  86     @Override
  87     protected Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags) {
  88         if (isNumericInteger(a.getPlatformKind())) {
  89             AArch64ArithmeticOp op = setFlags ? AArch64ArithmeticOp.SUBS : AArch64ArithmeticOp.SUB;
  90             return emitBinary(resultKind, op, false, a, b);
  91         } else {
  92             assert !setFlags : "Cannot set flags on floating point arithmetic";
  93             return emitBinary(resultKind, AArch64ArithmeticOp.FSUB, false, a, b);
  94         }
  95     }
  96 
  97     public Value emitExtendMemory(boolean isSigned, AArch64Kind memoryKind, int resultBits, AArch64AddressValue address, LIRFrameState state) {
  98         // Issue a zero extending load of the proper bit size and set the result to
  99         // the proper kind.
 100         Variable result = getLIRGen().newVariable(LIRKind.value(resultBits == 32 ? AArch64Kind.DWORD : AArch64Kind.QWORD));
 101 
 102         int targetSize = resultBits <= 32 ? 32 : 64;
 103         switch (memoryKind) {
 104             case BYTE:
 105             case WORD:
 106             case DWORD:
 107             case QWORD:
 108                 getLIRGen().append(new AArch64Unary.MemoryOp(isSigned, targetSize,
 109                                 memoryKind.getSizeInBytes() * 8, result, address, state));
 110                 break;
 111             default:
 112                 throw GraalError.shouldNotReachHere();
 113         }
 114         return result;
 115     }
 116 
 117     @Override
 118     public Value emitMul(Value a, Value b, boolean setFlags) {
 119         AArch64ArithmeticOp intOp = setFlags ? AArch64ArithmeticOp.MULVS : AArch64ArithmeticOp.MUL;
 120         return emitBinary(LIRKind.combine(a, b), getOpCode(a, intOp, AArch64ArithmeticOp.FMUL), true, a, b);
 121     }
 122 
 123     @Override
 124     public Value emitMulHigh(Value a, Value b) {
 125         assert isNumericInteger(a.getPlatformKind());
 126         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.SMULH, true, a, b);
 127     }
 128 
 129     @Override
 130     public Value emitUMulHigh(Value a, Value b) {
 131         assert isNumericInteger(a.getPlatformKind());
 132         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UMULH, true, a, b);
 133     }
 134 
 135     public Value emitMNeg(Value a, Value b) {
 136         assert isNumericInteger(a.getPlatformKind()) && isNumericInteger(b.getPlatformKind());
 137         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.MNEG, true, a, b);
 138     }
 139 
 140     @Override
 141     public Value emitDiv(Value a, Value b, LIRFrameState state) {
 142         return emitBinary(LIRKind.combine(a, b), getOpCode(a, AArch64ArithmeticOp.DIV, AArch64ArithmeticOp.FDIV), false, asAllocatable(a), asAllocatable(b));
 143     }
 144 
 145     @Override
 146     public Value emitRem(Value a, Value b, LIRFrameState state) {
 147         return emitBinary(LIRKind.combine(a, b), getOpCode(a, AArch64ArithmeticOp.REM, AArch64ArithmeticOp.FREM), false, asAllocatable(a), asAllocatable(b));
 148     }
 149 
 150     @Override
 151     public Value emitUDiv(Value a, Value b, LIRFrameState state) {
 152         assert isNumericInteger(a.getPlatformKind());
 153         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UDIV, false, asAllocatable(a), asAllocatable(b));
 154     }
 155 
 156     @Override
 157     public Value emitURem(Value a, Value b, LIRFrameState state) {
 158         assert isNumericInteger(a.getPlatformKind());
 159         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UREM, false, asAllocatable(a), asAllocatable(b));
 160     }
 161 
 162     @Override
 163     public Value emitAnd(Value a, Value b) {
 164         assert isNumericInteger(a.getPlatformKind());
 165         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.AND, true, a, b);
 166     }
 167 
 168     @Override
 169     public Value emitOr(Value a, Value b) {
 170         assert isNumericInteger(a.getPlatformKind());
 171         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.OR, true, a, b);
 172     }
 173 
 174     @Override
 175     public Value emitXor(Value a, Value b) {
 176         assert isNumericInteger(a.getPlatformKind());
 177         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.XOR, true, a, b);
 178     }
 179 
 180     @Override
 181     public Value emitShl(Value a, Value b) {
 182         assert isNumericInteger(a.getPlatformKind());
 183         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.SHL, false, a, b);
 184     }
 185 
 186     @Override
 187     public Value emitShr(Value a, Value b) {
 188         assert isNumericInteger(a.getPlatformKind());
 189         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.ASHR, false, a, b);
 190     }
 191 
 192     @Override
 193     public Value emitUShr(Value a, Value b) {
 194         assert isNumericInteger(a.getPlatformKind());
 195         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.LSHR, false, a, b);
 196     }
 197 
 198     @Override
 199     public Value emitFloatConvert(FloatConvert op, Value inputVal) {
 200         PlatformKind resultPlatformKind = getFloatConvertResultKind(op);
 201         LIRKind resultLirKind = LIRKind.combine(inputVal).changeType(resultPlatformKind);
 202         Variable result = getLIRGen().newVariable(resultLirKind);
 203         getLIRGen().append(new AArch64FloatConvertOp(op, result, asAllocatable(inputVal)));
 204         return result;
 205     }
 206 
 207     public Value emitMAdd(Value a, Value b, Value c) {
 208         return emitMultiplyAddSub(AArch64ArithmeticOp.ADD, a, b, c);
 209     }
 210 
 211     public Value emitMSub(Value a, Value b, Value c) {
 212         return emitMultiplyAddSub(AArch64ArithmeticOp.SUB, a, b, c);
 213     }
 214 
 215     private Value emitMultiplyAddSub(AArch64ArithmeticOp op, Value a, Value b, Value c) {
 216         assert isNumericInteger(a.getPlatformKind());
 217         assert isNumericInteger(b.getPlatformKind());
 218         assert isNumericInteger(c.getPlatformKind());
 219 
 220         Variable result = getLIRGen().newVariable(LIRKind.combine(a, b, c));
 221         AllocatableValue x = moveSp(asAllocatable(a));
 222         AllocatableValue y = moveSp(asAllocatable(b));
 223         AllocatableValue z = moveSp(asAllocatable(c));
 224         getLIRGen().append(new AArch64ArithmeticOp.MultiplyAddSubOp(op, result, x, y, z));
 225         return result;
 226     }
 227 
 228     private static PlatformKind getFloatConvertResultKind(FloatConvert op) {
 229         switch (op) {
 230             case F2I:
 231             case D2I:
 232                 return AArch64Kind.DWORD;
 233             case F2L:
 234             case D2L:
 235                 return AArch64Kind.QWORD;
 236             case I2F:
 237             case L2F:
 238             case D2F:
 239                 return AArch64Kind.SINGLE;
 240             case I2D:
 241             case L2D:
 242             case F2D:
 243                 return AArch64Kind.DOUBLE;
 244             default:
 245                 throw GraalError.shouldNotReachHere();
 246         }
 247     }
 248 
 249     @Override
 250     public Value emitReinterpret(LIRKind to, Value inputVal) {
 251         ValueKind<?> from = inputVal.getValueKind();
 252         if (to.equals(from)) {
 253             return inputVal;
 254         }
 255         Variable result = getLIRGen().newVariable(to);
 256         getLIRGen().append(new AArch64ReinterpretOp(result, asAllocatable(inputVal)));
 257         return result;
 258     }
 259 
 260     @Override
 261     public Value emitNarrow(Value inputVal, int bits) {
 262         if (inputVal.getPlatformKind() == AArch64Kind.QWORD && bits <= 32) {
 263             LIRKind resultKind = getResultLirKind(bits, inputVal);
 264             long mask = NumUtil.getNbitNumberLong(bits);
 265             Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
 266             return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
 267         } else {
 268             return inputVal;
 269         }
 270     }
 271 
 272     @Override
 273     public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
 274         assert fromBits <= toBits && toBits <= 64;
 275         if (fromBits == toBits) {
 276             return inputVal;
 277         }
 278         LIRKind resultKind = getResultLirKind(toBits, inputVal);
 279         long mask = NumUtil.getNbitNumberLong(fromBits);
 280         Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
 281         return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
 282     }
 283 
 284     @Override
 285     public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
 286         LIRKind resultKind = getResultLirKind(toBits, inputVal);
 287         assert fromBits <= toBits && toBits <= 64;
 288         if (fromBits == toBits) {
 289             return inputVal;
 290         } else if (isJavaConstant(inputVal)) {
 291             JavaConstant javaConstant = asJavaConstant(inputVal);
 292             long constant;
 293             if (javaConstant.isNull()) {
 294                 constant = 0;
 295             } else {
 296                 constant = javaConstant.asLong();
 297             }
 298             int shiftCount = QWORD.getSizeInBytes() * 8 - fromBits;
 299             return new ConstantValue(resultKind, JavaConstant.forLong((constant << shiftCount) >> shiftCount));
 300         }
 301         Variable result = getLIRGen().newVariable(resultKind);
 302         getLIRGen().append(new AArch64SignExtendOp(result, asAllocatable(inputVal), fromBits, toBits));
 303         return result;
 304     }
 305 
 306     private static LIRKind getResultLirKind(int resultBitSize, Value... inputValues) {
 307         if (resultBitSize == 64) {
 308             return LIRKind.combine(inputValues).changeType(QWORD);
 309         } else {
 310             // FIXME: I have no idea what this assert was ever for
 311             // assert resultBitSize == 32;
 312             return LIRKind.combine(inputValues).changeType(DWORD);
 313         }
 314     }
 315 
 316     protected Variable emitBinary(ValueKind<?> resultKind, AArch64ArithmeticOp op, boolean commutative, Value a, Value b) {
 317         Variable result = getLIRGen().newVariable(resultKind);
 318         if (isValidBinaryConstant(op, a, b)) {
 319             emitBinaryConst(result, op, asAllocatable(a), asJavaConstant(b));
 320         } else if (commutative && isValidBinaryConstant(op, b, a)) {
 321             emitBinaryConst(result, op, asAllocatable(b), asJavaConstant(a));
 322         } else {
 323             emitBinaryVar(result, op, asAllocatable(a), asAllocatable(b));
 324         }
 325         return result;
 326     }
 327 
 328     private void emitBinaryVar(Variable result, AArch64ArithmeticOp op, AllocatableValue a, AllocatableValue b) {
 329         AllocatableValue x = moveSp(a);
 330         AllocatableValue y = moveSp(b);
 331         switch (op) {
 332             case FREM:
 333             case REM:
 334             case UREM:
 335                 getLIRGen().append(new AArch64ArithmeticOp.BinaryCompositeOp(op, result, x, y));
 336                 break;
 337             default:
 338                 getLIRGen().append(new AArch64ArithmeticOp.BinaryOp(op, result, x, y));
 339                 break;
 340         }
 341     }
 342 
 343     private void emitBinaryConst(Variable result, AArch64ArithmeticOp op, AllocatableValue a, JavaConstant b) {
 344         AllocatableValue x = moveSp(a);
 345         getLIRGen().append(new AArch64ArithmeticOp.BinaryConstOp(op, result, x, b));
 346     }
 347 
 348     private static boolean isValidBinaryConstant(AArch64ArithmeticOp op, Value a, Value b) {
 349         if (!isJavaConstant(b)) {
 350             return false;
 351         }
 352         JavaConstant constValue = asJavaConstant(b);
 353         switch (op.category) {
 354             case LOGICAL:
 355                 return isLogicalConstant(constValue);
 356             case ARITHMETIC:
 357                 return isArithmeticConstant(constValue);
 358             case SHIFT:
 359                 assert constValue.asLong() >= 0 && constValue.asLong() < a.getPlatformKind().getSizeInBytes() * Byte.SIZE;
 360                 return true;
 361             case NONE:
 362                 return false;
 363             default:
 364                 throw GraalError.shouldNotReachHere();
 365         }
 366     }
 367 
 368     private static boolean isLogicalConstant(JavaConstant constValue) {
 369         switch (constValue.getJavaKind()) {
 370             case Int:
 371                 return AArch64MacroAssembler.isLogicalImmediate(constValue.asInt());
 372             case Long:
 373                 return AArch64MacroAssembler.isLogicalImmediate(constValue.asLong());
 374             default:
 375                 return false;
 376         }
 377     }
 378 
 379     protected static boolean isArithmeticConstant(JavaConstant constValue) {
 380         switch (constValue.getJavaKind()) {
 381             case Int:
 382             case Long:
 383                 return AArch64MacroAssembler.isArithmeticImmediate(constValue.asLong());
 384             case Object:
 385                 return constValue.isNull();
 386             default:
 387                 return false;
 388         }
 389     }
 390 
 391     @Override
 392     public Value emitNegate(Value inputVal) {
 393         return emitUnary(getOpCode(inputVal, AArch64ArithmeticOp.NEG, AArch64ArithmeticOp.FNEG), inputVal);
 394     }
 395 
 396     @Override
 397     public Value emitNot(Value input) {
 398         assert isNumericInteger(input.getPlatformKind());
 399         return emitUnary(AArch64ArithmeticOp.NOT, input);
 400     }
 401 
 402     @Override
 403     public Value emitMathAbs(Value input) {
 404         return emitUnary(getOpCode(input, AArch64ArithmeticOp.ABS, AArch64ArithmeticOp.FABS), input);
 405     }
 406 
 407     @Override
 408     public Value emitMathSqrt(Value input) {
 409         assert input.getPlatformKind() == AArch64Kind.DOUBLE;
 410         return emitUnary(AArch64ArithmeticOp.SQRT, input);
 411     }
 412 
 413     @Override
 414     public Variable emitBitScanForward(Value value) {
 415         throw GraalError.unimplemented();
 416     }
 417 
 418     @Override
 419     public Value emitBitCount(Value operand) {
 420         throw GraalError.unimplemented("AArch64 ISA does not offer way to implement this more efficiently than a simple Java algorithm.");
 421     }
 422 
 423     @Override
 424     public Value emitBitScanReverse(Value value) {
 425         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 426         getLIRGen().append(new AArch64BitManipulationOp(BSR, result, asAllocatable(value)));
 427         return result;
 428     }
 429 
 430     @Override
 431     public Value emitCountLeadingZeros(Value value) {
 432         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 433         getLIRGen().append(new AArch64BitManipulationOp(CLZ, result, asAllocatable(value)));
 434         return result;
 435     }
 436 
 437     @Override
 438     public Value emitCountTrailingZeros(Value value) {
 439         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 440         getLIRGen().append(new AArch64BitManipulationOp(CTZ, result, asAllocatable(value)));
 441         return result;
 442     }
 443 
 444     private Variable emitUnary(AArch64ArithmeticOp op, Value inputVal) {
 445         AllocatableValue input = asAllocatable(inputVal);
 446         Variable result = getLIRGen().newVariable(LIRKind.combine(input));
 447         getLIRGen().append(new AArch64ArithmeticOp.UnaryOp(op, result, input));
 448         return result;
 449     }
 450 
 451     private AllocatableValue moveSp(AllocatableValue val) {
 452         return getLIRGen().moveSp(val);
 453     }
 454 
 455     /**
 456      * Returns the opcode depending on the platform kind of val.
 457      */
 458     private AArch64ArithmeticOp getOpCode(Value val, AArch64ArithmeticOp intOp, AArch64ArithmeticOp floatOp) {
 459         return isNumericInteger(val.getPlatformKind()) ? intOp : floatOp;
 460     }
 461 
 462     @Override
 463     public Variable emitLoad(LIRKind kind, Value address, LIRFrameState state) {
 464         AArch64AddressValue loadAddress = getLIRGen().asAddressValue(address);
 465         Variable result = getLIRGen().newVariable(getLIRGen().toRegisterKind(kind));
 466         getLIRGen().append(new LoadOp((AArch64Kind) kind.getPlatformKind(), result, loadAddress, state));
 467         return result;
 468     }
 469 
 470     @Override
 471     public void emitStore(ValueKind<?> lirKind, Value address, Value inputVal, LIRFrameState state) {
 472         AArch64AddressValue storeAddress = getLIRGen().asAddressValue(address);
 473         AArch64Kind kind = (AArch64Kind) lirKind.getPlatformKind();
 474 
 475         if (isJavaConstant(inputVal) && kind.isInteger()) {
 476             JavaConstant c = asJavaConstant(inputVal);
 477             if (c.isDefaultForKind()) {
 478                 // We can load 0 directly into integer registers
 479                 getLIRGen().append(new StoreConstantOp(kind, storeAddress, c, state));
 480                 return;
 481             }
 482         }
 483         AllocatableValue input = asAllocatable(inputVal);
 484         getLIRGen().append(new StoreOp(kind, storeAddress, input, state));
 485     }
 486 
 487     @Override
 488     public void emitCompareOp(AArch64Kind cmpKind, Variable left, Value right) {
 489         throw GraalError.unimplemented();
 490     }
 491 
 492     @Override
 493     public Value emitRound(Value value, RoundingMode mode) {
 494         AArch64ArithmeticOp op;
 495         switch (mode) {
 496             case NEAREST:
 497                 op = AArch64ArithmeticOp.FRINTN;
 498                 break;
 499             case UP:
 500                 op = AArch64ArithmeticOp.FRINTP;
 501                 break;
 502             case DOWN:
 503                 op = AArch64ArithmeticOp.FRINTM;
 504                 break;
 505             default:
 506                 throw GraalError.shouldNotReachHere();
 507         }
 508 
 509         return emitUnary(op, value);
 510     }
 511 }