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 package org.graalvm.compiler.core.aarch64;
  25 
  26 import static jdk.vm.ci.aarch64.AArch64.sp;
  27 import static jdk.vm.ci.aarch64.AArch64Kind.DWORD;
  28 import static jdk.vm.ci.aarch64.AArch64Kind.QWORD;
  29 import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant;
  30 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
  31 import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.BSR;
  32 import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.CLZ;
  33 import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.CTZ;
  34 
  35 import org.graalvm.compiler.core.common.NumUtil;
  36 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
  37 import org.graalvm.compiler.core.common.LIRKind;
  38 import org.graalvm.compiler.core.common.calc.FloatConvert;
  39 import org.graalvm.compiler.debug.GraalError;
  40 import org.graalvm.compiler.lir.ConstantValue;
  41 import org.graalvm.compiler.lir.LIRFrameState;
  42 import org.graalvm.compiler.lir.Variable;
  43 import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
  44 import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool;
  45 import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp;
  46 import org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp;
  47 import org.graalvm.compiler.lir.aarch64.AArch64Move.LoadOp;
  48 import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreConstantOp;
  49 import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreOp;
  50 import org.graalvm.compiler.lir.aarch64.AArch64ReinterpretOp;
  51 import org.graalvm.compiler.lir.aarch64.AArch64SignExtendOp;
  52 import org.graalvm.compiler.lir.aarch64.AArch64Unary;
  53 import org.graalvm.compiler.lir.gen.ArithmeticLIRGenerator;
  54 
  55 import jdk.vm.ci.aarch64.AArch64Kind;
  56 import jdk.vm.ci.code.RegisterValue;
  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     @Override
 136     public Value emitDiv(Value a, Value b, LIRFrameState state) {
 137         return emitBinary(LIRKind.combine(a, b), getOpCode(a, AArch64ArithmeticOp.DIV, AArch64ArithmeticOp.FDIV), false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
 138     }
 139 
 140     @Override
 141     public Value emitRem(Value a, Value b, LIRFrameState state) {
 142         return emitBinary(LIRKind.combine(a, b), getOpCode(a, AArch64ArithmeticOp.REM, AArch64ArithmeticOp.FREM), false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
 143     }
 144 
 145     @Override
 146     public Value emitUDiv(Value a, Value b, LIRFrameState state) {
 147         assert isNumericInteger(a.getPlatformKind());
 148         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UDIV, false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
 149     }
 150 
 151     @Override
 152     public Value emitURem(Value a, Value b, LIRFrameState state) {
 153         assert isNumericInteger(a.getPlatformKind());
 154         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UREM, false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
 155     }
 156 
 157     @Override
 158     public Value emitAnd(Value a, Value b) {
 159         assert isNumericInteger(a.getPlatformKind());
 160         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.AND, true, a, b);
 161     }
 162 
 163     @Override
 164     public Value emitOr(Value a, Value b) {
 165         assert isNumericInteger(a.getPlatformKind());
 166         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.OR, true, a, b);
 167     }
 168 
 169     @Override
 170     public Value emitXor(Value a, Value b) {
 171         assert isNumericInteger(a.getPlatformKind());
 172         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.XOR, true, a, b);
 173     }
 174 
 175     @Override
 176     public Value emitShl(Value a, Value b) {
 177         assert isNumericInteger(a.getPlatformKind());
 178         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.SHL, false, a, b);
 179     }
 180 
 181     @Override
 182     public Value emitShr(Value a, Value b) {
 183         assert isNumericInteger(a.getPlatformKind());
 184         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.ASHR, false, a, b);
 185     }
 186 
 187     @Override
 188     public Value emitUShr(Value a, Value b) {
 189         assert isNumericInteger(a.getPlatformKind());
 190         return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.LSHR, false, a, b);
 191     }
 192 
 193     @Override
 194     public Value emitFloatConvert(FloatConvert op, Value inputVal) {
 195         PlatformKind resultPlatformKind = getFloatConvertResultKind(op);
 196         LIRKind resultLirKind = LIRKind.combine(inputVal).changeType(resultPlatformKind);
 197         Variable result = getLIRGen().newVariable(resultLirKind);
 198         getLIRGen().append(new AArch64FloatConvertOp(op, result, getLIRGen().asAllocatable(inputVal)));
 199         return result;
 200     }
 201 
 202     private static PlatformKind getFloatConvertResultKind(FloatConvert op) {
 203         switch (op) {
 204             case F2I:
 205             case D2I:
 206                 return AArch64Kind.DWORD;
 207             case F2L:
 208             case D2L:
 209                 return AArch64Kind.QWORD;
 210             case I2F:
 211             case L2F:
 212             case D2F:
 213                 return AArch64Kind.SINGLE;
 214             case I2D:
 215             case L2D:
 216             case F2D:
 217                 return AArch64Kind.DOUBLE;
 218             default:
 219                 throw GraalError.shouldNotReachHere();
 220         }
 221     }
 222 
 223     @Override
 224     public Value emitReinterpret(LIRKind to, Value inputVal) {
 225         ValueKind<?> from = inputVal.getValueKind();
 226         if (to.equals(from)) {
 227             return inputVal;
 228         }
 229         Variable result = getLIRGen().newVariable(to);
 230         getLIRGen().append(new AArch64ReinterpretOp(result, getLIRGen().asAllocatable(inputVal)));
 231         return result;
 232     }
 233 
 234     @Override
 235     public Value emitNarrow(Value inputVal, int bits) {
 236         if (inputVal.getPlatformKind() == AArch64Kind.QWORD && bits <= 32) {
 237             LIRKind resultKind = getResultLirKind(bits, inputVal);
 238             long mask = NumUtil.getNbitNumberLong(bits);
 239             Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
 240             return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
 241         } else {
 242             return inputVal;
 243         }
 244     }
 245 
 246     @Override
 247     public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
 248         assert fromBits <= toBits && toBits <= 64;
 249         if (fromBits == toBits) {
 250             return inputVal;
 251         }
 252         LIRKind resultKind = getResultLirKind(toBits, inputVal);
 253         long mask = NumUtil.getNbitNumberLong(fromBits);
 254         Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
 255         return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
 256     }
 257 
 258     @Override
 259     public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
 260         LIRKind resultKind = getResultLirKind(toBits, inputVal);
 261         assert fromBits <= toBits && toBits <= 64;
 262         if (fromBits == toBits) {
 263             return inputVal;
 264         } else if (isJavaConstant(inputVal)) {
 265             JavaConstant javaConstant = asJavaConstant(inputVal);
 266             long constant;
 267             if (javaConstant.isNull()) {
 268                 constant = 0;
 269             } else {
 270                 constant = javaConstant.asLong();
 271             }
 272             int shiftCount = QWORD.getSizeInBytes() * 8 - fromBits;
 273             return new ConstantValue(resultKind, JavaConstant.forLong((constant << shiftCount) >> shiftCount));
 274         }
 275         Variable result = getLIRGen().newVariable(resultKind);
 276         getLIRGen().append(new AArch64SignExtendOp(result, getLIRGen().asAllocatable(inputVal), fromBits, toBits));
 277         return result;
 278     }
 279 
 280     private static LIRKind getResultLirKind(int resultBitSize, Value... inputValues) {
 281         if (resultBitSize == 64) {
 282             return LIRKind.combine(inputValues).changeType(QWORD);
 283         } else {
 284             // FIXME: I have no idea what this assert was ever for
 285             // assert resultBitSize == 32;
 286             return LIRKind.combine(inputValues).changeType(DWORD);
 287         }
 288     }
 289 
 290     protected Variable emitBinary(ValueKind<?> resultKind, AArch64ArithmeticOp op, boolean commutative, Value a, Value b) {
 291         Variable result = getLIRGen().newVariable(resultKind);
 292         if (isValidBinaryConstant(op, a, b)) {
 293             emitBinaryConst(result, op, getLIRGen().asAllocatable(a), asJavaConstant(b));
 294         } else if (commutative && isValidBinaryConstant(op, b, a)) {
 295             emitBinaryConst(result, op, getLIRGen().asAllocatable(b), asJavaConstant(a));
 296         } else {
 297             emitBinaryVar(result, op, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
 298         }
 299         return result;
 300     }
 301 
 302     private void emitBinaryVar(Variable result, AArch64ArithmeticOp op, AllocatableValue a, AllocatableValue b) {
 303         AllocatableValue x = moveSp(a);
 304         AllocatableValue y = moveSp(b);
 305         switch (op) {
 306             case FREM:
 307             case REM:
 308             case UREM:
 309                 getLIRGen().append(new AArch64ArithmeticOp.BinaryCompositeOp(op, result, x, y));
 310                 break;
 311             default:
 312                 getLIRGen().append(new AArch64ArithmeticOp.BinaryOp(op, result, x, y));
 313                 break;
 314         }
 315     }
 316 
 317     private void emitBinaryConst(Variable result, AArch64ArithmeticOp op, AllocatableValue a, JavaConstant b) {
 318         AllocatableValue x = moveSp(a);
 319         getLIRGen().append(new AArch64ArithmeticOp.BinaryConstOp(op, result, x, b));
 320     }
 321 
 322     private static boolean isValidBinaryConstant(AArch64ArithmeticOp op, Value a, Value b) {
 323         if (!isJavaConstant(b)) {
 324             return false;
 325         }
 326         JavaConstant constValue = asJavaConstant(b);
 327         switch (op.category) {
 328             case LOGICAL:
 329                 return isLogicalConstant(constValue);
 330             case ARITHMETIC:
 331                 return isArithmeticConstant(constValue);
 332             case SHIFT:
 333                 assert constValue.asLong() >= 0 && constValue.asLong() < a.getPlatformKind().getSizeInBytes() * Byte.SIZE;
 334                 return true;
 335             case NONE:
 336                 return false;
 337             default:
 338                 throw GraalError.shouldNotReachHere();
 339         }
 340     }
 341 
 342     private static boolean isLogicalConstant(JavaConstant constValue) {
 343         switch (constValue.getJavaKind()) {
 344             case Int:
 345                 return AArch64MacroAssembler.isLogicalImmediate(constValue.asInt());
 346             case Long:
 347                 return AArch64MacroAssembler.isLogicalImmediate(constValue.asLong());
 348             default:
 349                 return false;
 350         }
 351     }
 352 
 353     protected static boolean isArithmeticConstant(JavaConstant constValue) {
 354         switch (constValue.getJavaKind()) {
 355             case Int:
 356             case Long:
 357                 return AArch64MacroAssembler.isArithmeticImmediate(constValue.asLong());
 358             case Object:
 359                 return constValue.isNull();
 360             default:
 361                 return false;
 362         }
 363     }
 364 
 365     @Override
 366     public Value emitNegate(Value inputVal) {
 367         return emitUnary(getOpCode(inputVal, AArch64ArithmeticOp.NEG, AArch64ArithmeticOp.FNEG), inputVal);
 368     }
 369 
 370     @Override
 371     public Value emitNot(Value input) {
 372         assert isNumericInteger(input.getPlatformKind());
 373         return emitUnary(AArch64ArithmeticOp.NOT, input);
 374     }
 375 
 376     @Override
 377     public Value emitMathAbs(Value input) {
 378         return emitUnary(getOpCode(input, AArch64ArithmeticOp.ABS, AArch64ArithmeticOp.FABS), input);
 379     }
 380 
 381     @Override
 382     public Value emitMathSqrt(Value input) {
 383         assert input.getPlatformKind() == AArch64Kind.DOUBLE;
 384         return emitUnary(AArch64ArithmeticOp.SQRT, input);
 385     }
 386 
 387     @Override
 388     public Variable emitBitScanForward(Value value) {
 389         throw GraalError.unimplemented();
 390     }
 391 
 392     @Override
 393     public Value emitBitCount(Value operand) {
 394         throw GraalError.unimplemented("AArch64 ISA does not offer way to implement this more efficiently than a simple Java algorithm.");
 395     }
 396 
 397     @Override
 398     public Value emitBitScanReverse(Value value) {
 399         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 400         getLIRGen().append(new AArch64BitManipulationOp(BSR, result, getLIRGen().asAllocatable(value)));
 401         return result;
 402     }
 403 
 404     @Override
 405     public Value emitCountLeadingZeros(Value value) {
 406         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 407         getLIRGen().append(new AArch64BitManipulationOp(CLZ, result, getLIRGen().asAllocatable(value)));
 408         return result;
 409     }
 410 
 411     @Override
 412     public Value emitCountTrailingZeros(Value value) {
 413         Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
 414         getLIRGen().append(new AArch64BitManipulationOp(CTZ, result, getLIRGen().asAllocatable(value)));
 415         return result;
 416     }
 417 
 418     private Variable emitUnary(AArch64ArithmeticOp op, Value inputVal) {
 419         AllocatableValue input = getLIRGen().asAllocatable(inputVal);
 420         Variable result = getLIRGen().newVariable(LIRKind.combine(input));
 421         getLIRGen().append(new AArch64ArithmeticOp.UnaryOp(op, result, input));
 422         return result;
 423     }
 424 
 425     /**
 426      * If val denotes the stackpointer, move it to another location. This is necessary since most
 427      * ops cannot handle the stackpointer as input or output.
 428      */
 429     private AllocatableValue moveSp(AllocatableValue val) {
 430         if (val instanceof RegisterValue && ((RegisterValue) val).getRegister().equals(sp)) {
 431             assert val.getPlatformKind() == AArch64Kind.QWORD : "Stackpointer must be long";
 432             return getLIRGen().emitMove(val);
 433         }
 434         return val;
 435     }
 436 
 437     /**
 438      * Returns the opcode depending on the platform kind of val.
 439      */
 440     private AArch64ArithmeticOp getOpCode(Value val, AArch64ArithmeticOp intOp, AArch64ArithmeticOp floatOp) {
 441         return isNumericInteger(val.getPlatformKind()) ? intOp : floatOp;
 442     }
 443 
 444     @Override
 445     public Variable emitLoad(LIRKind kind, Value address, LIRFrameState state) {
 446         AArch64AddressValue loadAddress = getLIRGen().asAddressValue(address);
 447         Variable result = getLIRGen().newVariable(getLIRGen().toRegisterKind(kind));
 448         getLIRGen().append(new LoadOp((AArch64Kind) kind.getPlatformKind(), result, loadAddress, state));
 449         return result;
 450     }
 451 
 452     @Override
 453     public void emitStore(ValueKind<?> lirKind, Value address, Value inputVal, LIRFrameState state) {
 454         AArch64AddressValue storeAddress = getLIRGen().asAddressValue(address);
 455         AArch64Kind kind = (AArch64Kind) lirKind.getPlatformKind();
 456 
 457         if (isJavaConstant(inputVal) && kind.isInteger()) {
 458             JavaConstant c = asJavaConstant(inputVal);
 459             if (c.isDefaultForKind()) {
 460                 // We can load 0 directly into integer registers
 461                 getLIRGen().append(new StoreConstantOp(kind, storeAddress, c, state));
 462                 return;
 463             }
 464         }
 465         AllocatableValue input = getLIRGen().asAllocatable(inputVal);
 466         getLIRGen().append(new StoreOp(kind, storeAddress, input, state));
 467     }
 468 
 469     @Override
 470     public Value emitMathLog(Value input, boolean base10) {
 471         throw GraalError.unimplemented();
 472     }
 473 
 474     @Override
 475     public Value emitMathCos(Value input) {
 476         throw GraalError.unimplemented();
 477     }
 478 
 479     @Override
 480     public Value emitMathSin(Value input) {
 481         throw GraalError.unimplemented();
 482     }
 483 
 484     @Override
 485     public Value emitMathTan(Value input) {
 486         throw GraalError.unimplemented();
 487     }
 488 
 489     @Override
 490     public void emitCompareOp(AArch64Kind cmpKind, Variable left, Value right) {
 491         throw GraalError.unimplemented();
 492     }
 493 
 494 }