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     private static PlatformKind getFloatConvertResultKind(FloatConvert op) {
 205         switch (op) {
 206             case F2I:
 207             case D2I:
 208                 return AArch64Kind.DWORD;
 209             case F2L:
 210             case D2L:
 211                 return AArch64Kind.QWORD;
 212             case I2F:
 213             case L2F:
 214             case D2F:
 215                 return AArch64Kind.SINGLE;
 216             case I2D:
 217             case L2D:
 218             case F2D:
 219                 return AArch64Kind.DOUBLE;
 220             default:
 221                 throw GraalError.shouldNotReachHere();
 222         }
 223     }
 224 
 225     @Override
 226     public Value emitReinterpret(LIRKind to, Value inputVal) {
 227         ValueKind<?> from = inputVal.getValueKind();
 228         if (to.equals(from)) {
 229             return inputVal;
 230         }
 231         Variable result = getLIRGen().newVariable(to);
 232         getLIRGen().append(new AArch64ReinterpretOp(result, asAllocatable(inputVal)));
 233         return result;
 234     }
 235 
 236     @Override
 237     public Value emitNarrow(Value inputVal, int bits) {
 238         if (inputVal.getPlatformKind() == AArch64Kind.QWORD && bits <= 32) {
 239             LIRKind resultKind = getResultLirKind(bits, inputVal);
 240             long mask = NumUtil.getNbitNumberLong(bits);
 241             Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
 242             return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
 243         } else {
 244             return inputVal;
 245         }
 246     }
 247 
 248     @Override
 249     public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
 250         assert fromBits <= toBits && toBits <= 64;
 251         if (fromBits == toBits) {
 252             return inputVal;
 253         }
 254         LIRKind resultKind = getResultLirKind(toBits, inputVal);
 255         long mask = NumUtil.getNbitNumberLong(fromBits);
 256         Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
 257         return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
 258     }
 259 
 260     @Override
 261     public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
 262         LIRKind resultKind = getResultLirKind(toBits, inputVal);
 263         assert fromBits <= toBits && toBits <= 64;
 264         if (fromBits == toBits) {
 265             return inputVal;
 266         } else if (isJavaConstant(inputVal)) {
 267             JavaConstant javaConstant = asJavaConstant(inputVal);
 268             long constant;
 269             if (javaConstant.isNull()) {
 270                 constant = 0;
 271             } else {
 272                 constant = javaConstant.asLong();
 273             }
 274             int shiftCount = QWORD.getSizeInBytes() * 8 - fromBits;
 275             return new ConstantValue(resultKind, JavaConstant.forLong((constant << shiftCount) >> shiftCount));
 276         }
 277         Variable result = getLIRGen().newVariable(resultKind);
 278         getLIRGen().append(new AArch64SignExtendOp(result, asAllocatable(inputVal), fromBits, toBits));
 279         return result;
 280     }
 281 
 282     private static LIRKind getResultLirKind(int resultBitSize, Value... inputValues) {
 283         if (resultBitSize == 64) {
 284             return LIRKind.combine(inputValues).changeType(QWORD);
 285         } else {
 286             // FIXME: I have no idea what this assert was ever for
 287             // assert resultBitSize == 32;
 288             return LIRKind.combine(inputValues).changeType(DWORD);
 289         }
 290     }
 291 
 292     protected Variable emitBinary(ValueKind<?> resultKind, AArch64ArithmeticOp op, boolean commutative, Value a, Value b) {
 293         Variable result = getLIRGen().newVariable(resultKind);
 294         if (isValidBinaryConstant(op, a, b)) {
 295             emitBinaryConst(result, op, asAllocatable(a), asJavaConstant(b));
 296         } else if (commutative && isValidBinaryConstant(op, b, a)) {
 297             emitBinaryConst(result, op, asAllocatable(b), asJavaConstant(a));
 298         } else {
 299             emitBinaryVar(result, op, asAllocatable(a), asAllocatable(b));
 300         }
 301         return result;
 302     }
 303 
 304     private void emitBinaryVar(Variable result, AArch64ArithmeticOp op, AllocatableValue a, AllocatableValue b) {
 305         AllocatableValue x = moveSp(a);
 306         AllocatableValue y = moveSp(b);
 307         switch (op) {
 308             case FREM:
 309             case REM:
 310             case UREM:
 311                 getLIRGen().append(new AArch64ArithmeticOp.BinaryCompositeOp(op, result, x, y));
 312                 break;
 313             default:
 314                 getLIRGen().append(new AArch64ArithmeticOp.BinaryOp(op, result, x, y));
 315                 break;
 316         }
 317     }
 318 
 319     private void emitBinaryConst(Variable result, AArch64ArithmeticOp op, AllocatableValue a, JavaConstant b) {
 320         AllocatableValue x = moveSp(a);
 321         getLIRGen().append(new AArch64ArithmeticOp.BinaryConstOp(op, result, x, b));
 322     }
 323 
 324     private static boolean isValidBinaryConstant(AArch64ArithmeticOp op, Value a, Value b) {
 325         if (!isJavaConstant(b)) {
 326             return false;
 327         }
 328         JavaConstant constValue = asJavaConstant(b);
 329         switch (op.category) {
 330             case LOGICAL:
 331                 return isLogicalConstant(constValue);
 332             case ARITHMETIC:
 333                 return isArithmeticConstant(constValue);
 334             case SHIFT:
 335                 assert constValue.asLong() >= 0 && constValue.asLong() < a.getPlatformKind().getSizeInBytes() * Byte.SIZE;
 336                 return true;
 337             case NONE:
 338                 return false;
 339             default:
 340                 throw GraalError.shouldNotReachHere();
 341         }
 342     }
 343 
 344     private static boolean isLogicalConstant(JavaConstant constValue) {
 345         switch (constValue.getJavaKind()) {
 346             case Int:
 347                 return AArch64MacroAssembler.isLogicalImmediate(constValue.asInt());
 348             case Long:
 349                 return AArch64MacroAssembler.isLogicalImmediate(constValue.asLong());
 350             default:
 351                 return false;
 352         }
 353     }
 354 
 355     protected static boolean isArithmeticConstant(JavaConstant constValue) {
 356         switch (constValue.getJavaKind()) {
 357             case Int:
 358             case Long:
 359                 return AArch64MacroAssembler.isArithmeticImmediate(constValue.asLong());
 360             case Object:
 361                 return constValue.isNull();
 362             default:
 363                 return false;
 364         }
 365     }
 366 
 367     @Override
 368     public Value emitNegate(Value inputVal) {
 369         return emitUnary(getOpCode(inputVal, AArch64ArithmeticOp.NEG, AArch64ArithmeticOp.FNEG), inputVal);
 370     }
 371 
 372     @Override
 373     public Value emitNot(Value input) {
 374         assert isNumericInteger(input.getPlatformKind());
 375         return emitUnary(AArch64ArithmeticOp.NOT, input);
 376     }
 377 
 378     @Override
 379     public Value emitMathAbs(Value input) {
 380         return emitUnary(getOpCode(input, AArch64ArithmeticOp.ABS, AArch64ArithmeticOp.FABS), input);
 381     }
 382 
 383     @Override
 384     public Value emitMathSqrt(Value input) {
 385         assert input.getPlatformKind() == AArch64Kind.DOUBLE;
 386         return emitUnary(AArch64ArithmeticOp.SQRT, input);
 387     }
 388 
 389     @Override
 390     public Variable emitBitScanForward(Value value) {
 391         throw GraalError.unimplemented();
 392     }
 393 
 394     @Override
 395     public Value emitBitCount(Value operand) {
 396         throw GraalError.unimplemented("AArch64 ISA does not offer way to implement this more efficiently than a simple Java algorithm.");
 397     }
 398 
 399     @Override
 400     public Value emitBitScanReverse(Value value) {
 401         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 402         getLIRGen().append(new AArch64BitManipulationOp(BSR, result, asAllocatable(value)));
 403         return result;
 404     }
 405 
 406     @Override
 407     public Value emitCountLeadingZeros(Value value) {
 408         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 409         getLIRGen().append(new AArch64BitManipulationOp(CLZ, result, asAllocatable(value)));
 410         return result;
 411     }
 412 
 413     @Override
 414     public Value emitCountTrailingZeros(Value value) {
 415         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 416         getLIRGen().append(new AArch64BitManipulationOp(CTZ, result, asAllocatable(value)));
 417         return result;
 418     }
 419 
 420     private Variable emitUnary(AArch64ArithmeticOp op, Value inputVal) {
 421         AllocatableValue input = asAllocatable(inputVal);
 422         Variable result = getLIRGen().newVariable(LIRKind.combine(input));
 423         getLIRGen().append(new AArch64ArithmeticOp.UnaryOp(op, result, input));
 424         return result;
 425     }
 426 
 427     /**
 428      * If val denotes the stackpointer, move it to another location. This is necessary since most
 429      * ops cannot handle the stackpointer as input or output.
 430      */
 431     private AllocatableValue moveSp(AllocatableValue val) {
 432         if (val instanceof RegisterValue && ((RegisterValue) val).getRegister().equals(sp)) {
 433             assert val.getPlatformKind() == AArch64Kind.QWORD : "Stackpointer must be long";
 434             return getLIRGen().emitMove(val);
 435         }
 436         return val;
 437     }
 438 
 439     /**
 440      * Returns the opcode depending on the platform kind of val.
 441      */
 442     private AArch64ArithmeticOp getOpCode(Value val, AArch64ArithmeticOp intOp, AArch64ArithmeticOp floatOp) {
 443         return isNumericInteger(val.getPlatformKind()) ? intOp : floatOp;
 444     }
 445 
 446     @Override
 447     public Variable emitLoad(LIRKind kind, Value address, LIRFrameState state) {
 448         AArch64AddressValue loadAddress = getLIRGen().asAddressValue(address);
 449         Variable result = getLIRGen().newVariable(getLIRGen().toRegisterKind(kind));
 450         getLIRGen().append(new LoadOp((AArch64Kind) kind.getPlatformKind(), result, loadAddress, state));
 451         return result;
 452     }
 453 
 454     @Override
 455     public void emitStore(ValueKind<?> lirKind, Value address, Value inputVal, LIRFrameState state) {
 456         AArch64AddressValue storeAddress = getLIRGen().asAddressValue(address);
 457         AArch64Kind kind = (AArch64Kind) lirKind.getPlatformKind();
 458 
 459         if (isJavaConstant(inputVal) && kind.isInteger()) {
 460             JavaConstant c = asJavaConstant(inputVal);
 461             if (c.isDefaultForKind()) {
 462                 // We can load 0 directly into integer registers
 463                 getLIRGen().append(new StoreConstantOp(kind, storeAddress, c, state));
 464                 return;
 465             }
 466         }
 467         AllocatableValue input = asAllocatable(inputVal);
 468         getLIRGen().append(new StoreOp(kind, storeAddress, input, state));
 469     }
 470 
 471     @Override
 472     public Value emitMathLog(Value input, boolean base10) {
 473         throw GraalError.unimplemented();
 474     }
 475 
 476     @Override
 477     public Value emitMathCos(Value input) {
 478         throw GraalError.unimplemented();
 479     }
 480 
 481     @Override
 482     public Value emitMathSin(Value input) {
 483         throw GraalError.unimplemented();
 484     }
 485 
 486     @Override
 487     public Value emitMathTan(Value input) {
 488         throw GraalError.unimplemented();
 489     }
 490 
 491     @Override
 492     public void emitCompareOp(AArch64Kind cmpKind, Variable left, Value right) {
 493         throw GraalError.unimplemented();
 494     }
 495 
 496     @Override
 497     public Value emitRound(Value value, RoundingMode mode) {
 498         AArch64ArithmeticOp op;
 499         switch (mode) {
 500             case NEAREST:
 501                 op = AArch64ArithmeticOp.FRINTN;
 502                 break;
 503             case UP:
 504                 op = AArch64ArithmeticOp.FRINTP;
 505                 break;
 506             case DOWN:
 507                 op = AArch64ArithmeticOp.FRINTM;
 508                 break;
 509             default:
 510                 throw GraalError.shouldNotReachHere();
 511         }
 512 
 513         return emitUnary(op, value);
 514     }
 515 }