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 Value emitMathLog(Value input, boolean base10) { 489 throw GraalError.unimplemented(); 490 } 491 492 @Override 493 public Value emitMathCos(Value input) { 494 throw GraalError.unimplemented(); 495 } 496 497 @Override 498 public Value emitMathSin(Value input) { 499 throw GraalError.unimplemented(); 500 } 501 502 @Override 503 public Value emitMathTan(Value input) { 504 throw GraalError.unimplemented(); 505 } 506 507 @Override 508 public void emitCompareOp(AArch64Kind cmpKind, Variable left, Value right) { 509 throw GraalError.unimplemented(); 510 } 511 512 @Override 513 public Value emitRound(Value value, RoundingMode mode) { 514 AArch64ArithmeticOp op; 515 switch (mode) { 516 case NEAREST: 517 op = AArch64ArithmeticOp.FRINTN; 518 break; 519 case UP: 520 op = AArch64ArithmeticOp.FRINTP; 521 break; 522 case DOWN: 523 op = AArch64ArithmeticOp.FRINTM; 524 break; 525 default: 526 throw GraalError.shouldNotReachHere(); 527 } 528 529 return emitUnary(op, value); 530 } 531 }