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