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