| /* |
| * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package org.graalvm.compiler.lir.aarch64; |
| |
| import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; |
| import static org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp.ARMv8ConstantCategory.ARITHMETIC; |
| import static org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp.ARMv8ConstantCategory.LOGICAL; |
| import static org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp.ARMv8ConstantCategory.NONE; |
| import static org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp.ARMv8ConstantCategory.SHIFT; |
| import static jdk.vm.ci.aarch64.AArch64.zr; |
| import static jdk.vm.ci.code.ValueUtil.asRegister; |
| |
| import org.graalvm.compiler.asm.aarch64.AArch64Assembler; |
| import org.graalvm.compiler.asm.aarch64.AArch64Assembler.ConditionFlag; |
| import org.graalvm.compiler.debug.GraalError; |
| import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler; |
| import org.graalvm.compiler.lir.LIRInstructionClass; |
| import org.graalvm.compiler.lir.Opcode; |
| import org.graalvm.compiler.lir.asm.CompilationResultBuilder; |
| |
| import jdk.vm.ci.code.Register; |
| import jdk.vm.ci.meta.AllocatableValue; |
| import jdk.vm.ci.meta.JavaConstant; |
| |
| public enum AArch64ArithmeticOp { |
| // TODO At least add and sub *can* be used with SP, so this should be supported |
| NEG, |
| NOT, |
| ADD(ARITHMETIC), |
| ADDS(ARITHMETIC), |
| SUB(ARITHMETIC), |
| SUBS(ARITHMETIC), |
| MUL, |
| DIV, |
| SMULH, |
| UMULH, |
| REM, |
| UDIV, |
| UREM, |
| AND(LOGICAL), |
| ANDS(LOGICAL), |
| OR(LOGICAL), |
| XOR(LOGICAL), |
| SHL(SHIFT), |
| LSHR(SHIFT), |
| ASHR(SHIFT), |
| ABS, |
| |
| FADD, |
| FSUB, |
| FMUL, |
| FDIV, |
| FREM, |
| FNEG, |
| FABS, |
| SQRT; |
| |
| /** |
| * Specifies what constants can be used directly without having to be loaded into a register |
| * with the given instruction. |
| */ |
| public enum ARMv8ConstantCategory { |
| NONE, |
| LOGICAL, |
| ARITHMETIC, |
| SHIFT |
| } |
| |
| public final ARMv8ConstantCategory category; |
| |
| AArch64ArithmeticOp(ARMv8ConstantCategory category) { |
| this.category = category; |
| } |
| |
| AArch64ArithmeticOp() { |
| this(NONE); |
| } |
| |
| public static class UnaryOp extends AArch64LIRInstruction { |
| private static final LIRInstructionClass<UnaryOp> TYPE = LIRInstructionClass.create(UnaryOp.class); |
| |
| @Opcode private final AArch64ArithmeticOp opcode; |
| @Def({REG}) protected AllocatableValue result; |
| @Use({REG}) protected AllocatableValue x; |
| |
| public UnaryOp(AArch64ArithmeticOp opcode, AllocatableValue result, AllocatableValue x) { |
| super(TYPE); |
| this.opcode = opcode; |
| this.result = result; |
| this.x = x; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { |
| Register dst = asRegister(result); |
| Register src = asRegister(x); |
| int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE; |
| switch (opcode) { |
| case NEG: |
| masm.sub(size, dst, zr, src); |
| break; |
| case FNEG: |
| masm.fneg(size, dst, src); |
| break; |
| case NOT: |
| masm.not(size, dst, src); |
| break; |
| case ABS: |
| masm.cmp(size, src, 0); |
| masm.csneg(size, dst, src, ConditionFlag.LT); |
| break; |
| case FABS: |
| masm.fabs(size, dst, src); |
| break; |
| case SQRT: |
| masm.fsqrt(size, dst, src); |
| break; |
| default: |
| throw GraalError.shouldNotReachHere("op=" + opcode.name()); |
| } |
| } |
| } |
| |
| public static class BinaryConstOp extends AArch64LIRInstruction { |
| private static final LIRInstructionClass<BinaryConstOp> TYPE = LIRInstructionClass.create(BinaryConstOp.class); |
| |
| @Opcode private final AArch64ArithmeticOp op; |
| @Def({REG}) protected AllocatableValue result; |
| @Use({REG}) protected AllocatableValue a; |
| private final JavaConstant b; |
| |
| public BinaryConstOp(AArch64ArithmeticOp op, AllocatableValue result, AllocatableValue a, JavaConstant b) { |
| super(TYPE); |
| this.op = op; |
| this.result = result; |
| this.a = a; |
| this.b = b; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { |
| assert op.category != NONE; |
| Register dst = asRegister(result); |
| Register src = asRegister(a); |
| int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE; |
| switch (op) { |
| case ADD: |
| // Don't use asInt() here, since we can't use asInt on a long variable, even |
| // if the constant easily fits as an int. |
| assert AArch64MacroAssembler.isArithmeticImmediate(b.asLong()); |
| masm.add(size, dst, src, (int) b.asLong()); |
| break; |
| case SUB: |
| // Don't use asInt() here, since we can't use asInt on a long variable, even |
| // if the constant easily fits as an int. |
| assert AArch64MacroAssembler.isArithmeticImmediate(b.asLong()); |
| masm.sub(size, dst, src, (int) b.asLong()); |
| break; |
| case AND: |
| // XXX Should this be handled somewhere else? |
| if (size == 32 && b.asLong() == 0xFFFF_FFFFL) { |
| masm.mov(size, dst, src); |
| } else { |
| masm.and(size, dst, src, b.asLong()); |
| } |
| break; |
| case ANDS: |
| masm.ands(size, dst, src, b.asLong()); |
| break; |
| case OR: |
| masm.or(size, dst, src, b.asLong()); |
| break; |
| case XOR: |
| masm.eor(size, dst, src, b.asLong()); |
| break; |
| case SHL: |
| masm.shl(size, dst, src, b.asLong()); |
| break; |
| case LSHR: |
| masm.lshr(size, dst, src, b.asLong()); |
| break; |
| case ASHR: |
| masm.ashr(size, dst, src, b.asLong()); |
| break; |
| default: |
| throw GraalError.shouldNotReachHere("op=" + op.name()); |
| } |
| } |
| } |
| |
| public static class BinaryOp extends AArch64LIRInstruction { |
| private static final LIRInstructionClass<BinaryOp> TYPE = LIRInstructionClass.create(BinaryOp.class); |
| |
| @Opcode private final AArch64ArithmeticOp op; |
| @Def({REG}) protected AllocatableValue result; |
| @Use({REG}) protected AllocatableValue a; |
| @Use({REG}) protected AllocatableValue b; |
| |
| public BinaryOp(AArch64ArithmeticOp op, AllocatableValue result, AllocatableValue a, AllocatableValue b) { |
| super(TYPE); |
| this.op = op; |
| this.result = result; |
| this.a = a; |
| this.b = b; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { |
| Register dst = asRegister(result); |
| Register src1 = asRegister(a); |
| Register src2 = asRegister(b); |
| int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE; |
| switch (op) { |
| case ADD: |
| masm.add(size, dst, src1, src2); |
| break; |
| case ADDS: |
| masm.adds(size, dst, src1, src2); |
| break; |
| case SUB: |
| masm.sub(size, dst, src1, src2); |
| break; |
| case SUBS: |
| masm.subs(size, dst, src1, src2); |
| break; |
| case MUL: |
| masm.mul(size, dst, src1, src2); |
| break; |
| case UMULH: |
| masm.umulh(size, dst, src1, src2); |
| break; |
| case SMULH: |
| masm.smulh(size, dst, src1, src2); |
| break; |
| case DIV: |
| masm.sdiv(size, dst, src1, src2); |
| break; |
| case UDIV: |
| masm.udiv(size, dst, src1, src2); |
| break; |
| case AND: |
| masm.and(size, dst, src1, src2); |
| break; |
| case ANDS: |
| masm.ands(size, dst, src1, src2); |
| break; |
| case OR: |
| masm.or(size, dst, src1, src2); |
| break; |
| case XOR: |
| masm.eor(size, dst, src1, src2); |
| break; |
| case SHL: |
| masm.shl(size, dst, src1, src2); |
| break; |
| case LSHR: |
| masm.lshr(size, dst, src1, src2); |
| break; |
| case ASHR: |
| masm.ashr(size, dst, src1, src2); |
| break; |
| case FADD: |
| masm.fadd(size, dst, src1, src2); |
| break; |
| case FSUB: |
| masm.fsub(size, dst, src1, src2); |
| break; |
| case FMUL: |
| masm.fmul(size, dst, src1, src2); |
| break; |
| case FDIV: |
| masm.fdiv(size, dst, src1, src2); |
| break; |
| default: |
| throw GraalError.shouldNotReachHere("op=" + op.name()); |
| } |
| } |
| } |
| |
| /** |
| * Class used for instructions that have to reuse one of their arguments. This only applies to |
| * the remainder instructions at the moment, since we have to compute n % d using rem = n - |
| * TruncatingDivision(n, d) * d |
| * |
| * TODO (das) Replace the remainder nodes in the LIR. |
| */ |
| public static class BinaryCompositeOp extends AArch64LIRInstruction { |
| private static final LIRInstructionClass<BinaryCompositeOp> TYPE = LIRInstructionClass.create(BinaryCompositeOp.class); |
| @Opcode private final AArch64ArithmeticOp op; |
| @Def({REG}) protected AllocatableValue result; |
| @Alive({REG}) protected AllocatableValue a; |
| @Alive({REG}) protected AllocatableValue b; |
| |
| public BinaryCompositeOp(AArch64ArithmeticOp op, AllocatableValue result, AllocatableValue a, AllocatableValue b) { |
| super(TYPE); |
| this.op = op; |
| this.result = result; |
| this.a = a; |
| this.b = b; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { |
| Register dst = asRegister(result); |
| Register src1 = asRegister(a); |
| Register src2 = asRegister(b); |
| int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE; |
| switch (op) { |
| case REM: |
| masm.rem(size, dst, src1, src2); |
| break; |
| case UREM: |
| masm.urem(size, dst, src1, src2); |
| break; |
| case FREM: |
| masm.frem(size, dst, src1, src2); |
| break; |
| default: |
| throw GraalError.shouldNotReachHere(); |
| } |
| } |
| } |
| |
| public static class AddSubShiftOp extends AArch64LIRInstruction { |
| private static final LIRInstructionClass<AddSubShiftOp> TYPE = LIRInstructionClass.create(AddSubShiftOp.class); |
| |
| @Opcode private final AArch64ArithmeticOp op; |
| @Def(REG) protected AllocatableValue result; |
| @Use(REG) protected AllocatableValue src1; |
| @Use(REG) protected AllocatableValue src2; |
| private final AArch64MacroAssembler.ShiftType shiftType; |
| private final int shiftAmt; |
| |
| /** |
| * Computes <code>result = src1 <op> src2 <shiftType> <shiftAmt></code>. |
| */ |
| public AddSubShiftOp(AArch64ArithmeticOp op, AllocatableValue result, AllocatableValue src1, AllocatableValue src2, AArch64MacroAssembler.ShiftType shiftType, int shiftAmt) { |
| super(TYPE); |
| assert op == ADD || op == SUB; |
| this.op = op; |
| this.result = result; |
| this.src1 = src1; |
| this.src2 = src2; |
| this.shiftType = shiftType; |
| this.shiftAmt = shiftAmt; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { |
| int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE; |
| switch (op) { |
| case ADD: |
| masm.add(size, asRegister(result), asRegister(src1), asRegister(src2), shiftType, shiftAmt); |
| break; |
| case SUB: |
| masm.sub(size, asRegister(result), asRegister(src1), asRegister(src2), shiftType, shiftAmt); |
| break; |
| default: |
| throw GraalError.shouldNotReachHere(); |
| } |
| } |
| } |
| |
| public static class ExtendedAddShiftOp extends AArch64LIRInstruction { |
| private static final LIRInstructionClass<ExtendedAddShiftOp> TYPE = LIRInstructionClass.create(ExtendedAddShiftOp.class); |
| @Def(REG) protected AllocatableValue result; |
| @Use(REG) protected AllocatableValue src1; |
| @Use(REG) protected AllocatableValue src2; |
| private final AArch64Assembler.ExtendType extendType; |
| private final int shiftAmt; |
| |
| /** |
| * Computes <code>result = src1 + extendType(src2) << shiftAmt</code>. |
| * |
| * @param extendType defines how src2 is extended to the same size as src1. |
| * @param shiftAmt must be in range 0 to 4. |
| */ |
| public ExtendedAddShiftOp(AllocatableValue result, AllocatableValue src1, AllocatableValue src2, AArch64Assembler.ExtendType extendType, int shiftAmt) { |
| super(TYPE); |
| this.result = result; |
| this.src1 = src1; |
| this.src2 = src2; |
| this.extendType = extendType; |
| this.shiftAmt = shiftAmt; |
| } |
| |
| @Override |
| public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { |
| int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE; |
| masm.add(size, asRegister(result), asRegister(src1), asRegister(src2), extendType, shiftAmt); |
| } |
| } |
| |
| } |