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 }