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 }