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 }