let ParserMatchClass = MoveVecShifterOperand;
}
-def AddSubImmOperand : AsmOperandClass {
- let Name = "AddSubImm";
- let ParserMethod = "tryParseAddSubImm";
- let DiagnosticType = "AddSubSecondSource";
+let DiagnosticType = "AddSubSecondSource" in {
+ def AddSubImmOperand : AsmOperandClass {
+ let Name = "AddSubImm";
+ let ParserMethod = "tryParseAddSubImm";
+ }
+ def AddSubImmNegOperand : AsmOperandClass {
+ let Name = "AddSubImmNeg";
+ let ParserMethod = "tryParseAddSubImm";
+ }
}
// An ADD/SUB immediate shifter operand:
// second operand:
let MIOperandInfo = (ops i32imm, i32imm);
}
+class addsub_shifted_imm_neg<ValueType Ty>
+ : Operand<Ty> {
+ let EncoderMethod = "getAddSubImmOpValue";
+ let ParserMatchClass = AddSubImmNegOperand;
+ let MIOperandInfo = (ops i32imm, i32imm);
+}
+
def addsub_shifted_imm32 : addsub_shifted_imm<i32>;
def addsub_shifted_imm64 : addsub_shifted_imm<i64>;
+def addsub_shifted_imm32_neg : addsub_shifted_imm_neg<i32>;
+def addsub_shifted_imm64_neg : addsub_shifted_imm_neg<i64>;
class neg_addsub_shifted_imm<ValueType Ty>
: Operand<Ty>, ComplexPattern<Ty, 2, "SelectNegArithImmed", [imm]> {
(inst dstRegtype:$dst, src1Regtype:$src1, src2Regtype:$src2,
shiftExt)>;
-multiclass AddSub<bit isSub, string mnemonic,
+multiclass AddSub<bit isSub, string mnemonic, string alias,
SDPatternOperator OpNode = null_frag> {
let hasSideEffects = 0, isReMaterializable = 1, isAsCheapAsAMove = 1 in {
// Add/Subtract immediate
let Inst{31} = 1;
}
+ // add Rd, Rb, -imm -> sub Rd, Rn, imm
+ def : InstAlias<alias#" $Rd, $Rn, $imm",
+ (!cast<Instruction>(NAME # "Wri") GPR32sp:$Rd, GPR32sp:$Rn,
+ addsub_shifted_imm32_neg:$imm), 0>;
+ def : InstAlias<alias#" $Rd, $Rn, $imm",
+ (!cast<Instruction>(NAME # "Xri") GPR64sp:$Rd, GPR64sp:$Rn,
+ addsub_shifted_imm64_neg:$imm), 0>;
+
// Register/register aliases with no shift when SP is not used.
def : AddSubRegAlias<mnemonic, !cast<Instruction>(NAME#"Wrs"),
GPR32, GPR32, GPR32, 0>;
GPR64sp, GPR64sponly, GPR64, 24>; // UXTX #0
}
-multiclass AddSubS<bit isSub, string mnemonic, SDNode OpNode, string cmp> {
+multiclass AddSubS<bit isSub, string mnemonic, SDNode OpNode, string cmp,
+ string alias, string cmpAlias> {
let isCompare = 1, Defs = [NZCV] in {
// Add/Subtract immediate
def Wri : BaseAddSubImm<isSub, 1, GPR32, GPR32sp, addsub_shifted_imm32,
}
} // Defs = [NZCV]
+ // Support negative immediates, e.g. adds Rd, Rn, -imm -> subs Rd, Rn, imm
+ def : InstAlias<alias#" $Rd, $Rn, $imm",
+ (!cast<Instruction>(NAME # "Wri") GPR32:$Rd, GPR32sp:$Rn,
+ addsub_shifted_imm32_neg:$imm), 0>;
+ def : InstAlias<alias#" $Rd, $Rn, $imm",
+ (!cast<Instruction>(NAME # "Xri") GPR64:$Rd, GPR64sp:$Rn,
+ addsub_shifted_imm64_neg:$imm), 0>;
+
// Compare aliases
def : InstAlias<cmp#" $src, $imm", (!cast<Instruction>(NAME#"Wri")
WZR, GPR32sp:$src, addsub_shifted_imm32:$imm), 5>;
def : InstAlias<cmp#" $src1, $src2$sh", (!cast<Instruction>(NAME#"Xrs")
XZR, GPR64:$src1, GPR64:$src2, arith_shift64:$sh), 4>;
+ // Support negative immediates, e.g. cmp Rn, -imm -> cmn Rn, imm
+ def : InstAlias<cmpAlias#" $src, $imm", (!cast<Instruction>(NAME#"Wri")
+ WZR, GPR32sp:$src, addsub_shifted_imm32_neg:$imm), 0>;
+ def : InstAlias<cmpAlias#" $src, $imm", (!cast<Instruction>(NAME#"Xri")
+ XZR, GPR64sp:$src, addsub_shifted_imm64_neg:$imm), 0>;
+
// Compare shorthands
def : InstAlias<cmp#" $src1, $src2", (!cast<Instruction>(NAME#"Wrs")
WZR, GPR32:$src1, GPR32:$src2, 0), 5>;
def : InstAlias<"ngcs $dst, $src", (SBCSXr GPR64:$dst, XZR, GPR64:$src)>;
// Add/subtract
-defm ADD : AddSub<0, "add", add>;
-defm SUB : AddSub<1, "sub">;
+defm ADD : AddSub<0, "add", "sub", add>;
+defm SUB : AddSub<1, "sub", "add">;
def : InstAlias<"mov $dst, $src",
(ADDWri GPR32sponly:$dst, GPR32sp:$src, 0, 0)>;
def : InstAlias<"mov $dst, $src",
(ADDXri GPR64sp:$dst, GPR64sponly:$src, 0, 0)>;
-defm ADDS : AddSubS<0, "adds", AArch64add_flag, "cmn">;
-defm SUBS : AddSubS<1, "subs", AArch64sub_flag, "cmp">;
+defm ADDS : AddSubS<0, "adds", AArch64add_flag, "cmn", "subs", "cmp">;
+defm SUBS : AddSubS<1, "subs", AArch64sub_flag, "cmp", "adds", "cmn">;
// Use SUBS instead of SUB to enable CSE between SUBS and SUB.
def : Pat<(sub GPR32sp:$Rn, addsub_shifted_imm32:$imm),
const MCConstantExpr *CE = cast<MCConstantExpr>(Expr);
return CE->getValue() >= 0 && CE->getValue() <= 0xfff;
}
+ bool isAddSubImmNeg() const {
+ if (!isShiftedImm() && !isImm())
+ return false;
+
+ const MCExpr *Expr;
+
+ // An ADD/SUB shifter is either 'lsl #0' or 'lsl #12'.
+ if (isShiftedImm()) {
+ unsigned Shift = ShiftedImm.ShiftAmount;
+ Expr = ShiftedImm.Val;
+ if (Shift != 0 && Shift != 12)
+ return false;
+ } else
+ Expr = getImm();
+
+ // Otherwise it should be a real negative immediate in range:
+ const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr);
+ return CE != nullptr && CE->getValue() < 0 && -CE->getValue() <= 0xfff;
+ }
bool isCondCode() const { return Kind == k_CondCode; }
bool isSIMDImmType10() const {
if (!isImm())
}
}
+ void addAddSubImmNegOperands(MCInst &Inst, unsigned N) const {
+ assert(N == 2 && "Invalid number of operands!");
+
+ const MCExpr *MCE = isShiftedImm() ? getShiftedImmVal() : getImm();
+ const MCConstantExpr *CE = cast<MCConstantExpr>(MCE);
+ int64_t Val = -CE->getValue();
+ unsigned ShiftAmt = isShiftedImm() ? ShiftedImm.ShiftAmount : 0;
+
+ Inst.addOperand(MCOperand::createImm(Val));
+ Inst.addOperand(MCOperand::createImm(ShiftAmt));
+ }
+
void addCondCodeOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createImm(getCondCode()));
--- /dev/null
+// RUN: llvm-mc -triple=aarch64-none-linux-gnu < %s | FileCheck %s
+
+// CHECK: sub w0, w2, #2, lsl #12
+// CHECK: sub w0, w2, #2, lsl #12
+ sub w0, w2, #2, lsl 12
+ add w0, w2, #-2, lsl 12
+// CHECK: sub x1, x3, #2, lsl #12
+// CHECK: sub x1, x3, #2, lsl #12
+ sub x1, x3, #2, lsl 12
+ add x1, x3, #-2, lsl 12
+// CHECK: sub x1, x3, #4
+// CHECK: sub x1, x3, #4
+ sub x1, x3, #4
+ add x1, x3, #-4
+// CHECK: sub x1, x3, #4095
+// CHECK: sub x1, x3, #4095
+ sub x1, x3, #4095, lsl 0
+ add x1, x3, #-4095, lsl 0
+// CHECK: sub x3, x4, #0
+ sub x3, x4, #0
+
+// CHECK: add w0, w2, #2, lsl #12
+// CHECK: add w0, w2, #2, lsl #12
+ add w0, w2, #2, lsl 12
+ sub w0, w2, #-2, lsl 12
+// CHECK: add x1, x3, #2, lsl #12
+// CHECK: add x1, x3, #2, lsl #12
+ add x1, x3, #2, lsl 12
+ sub x1, x3, #-2, lsl 12
+// CHECK: add x1, x3, #4
+// CHECK: add x1, x3, #4
+ add x1, x3, #4
+ sub x1, x3, #-4
+// CHECK: add x1, x3, #4095
+// CHECK: add x1, x3, #4095
+ add x1, x3, #4095, lsl 0
+ sub x1, x3, #-4095, lsl 0
+// CHECK: add x2, x5, #0
+ add x2, x5, #0
+
+// CHECK: subs w0, w2, #2, lsl #12
+// CHECK: subs w0, w2, #2, lsl #12
+ subs w0, w2, #2, lsl 12
+ adds w0, w2, #-2, lsl 12
+// CHECK: subs x1, x3, #2, lsl #12
+// CHECK: subs x1, x3, #2, lsl #12
+ subs x1, x3, #2, lsl 12
+ adds x1, x3, #-2, lsl 12
+// CHECK: subs x1, x3, #4
+// CHECK: subs x1, x3, #4
+ subs x1, x3, #4
+ adds x1, x3, #-4
+// CHECK: subs x1, x3, #4095
+// CHECK: subs x1, x3, #4095
+ subs x1, x3, #4095, lsl 0
+ adds x1, x3, #-4095, lsl 0
+// CHECK: subs x3, x4, #0
+ subs x3, x4, #0
+
+// CHECK: adds w0, w2, #2, lsl #12
+// CHECK: adds w0, w2, #2, lsl #12
+ adds w0, w2, #2, lsl 12
+ subs w0, w2, #-2, lsl 12
+// CHECK: adds x1, x3, #2, lsl #12
+// CHECK: adds x1, x3, #2, lsl #12
+ adds x1, x3, #2, lsl 12
+ subs x1, x3, #-2, lsl 12
+// CHECK: adds x1, x3, #4
+// CHECK: adds x1, x3, #4
+ adds x1, x3, #4
+ subs x1, x3, #-4
+// CHECK: adds x1, x3, #4095
+// CHECK: adds x1, x3, #4095
+ adds x1, x3, #4095, lsl 0
+ subs x1, x3, #-4095, lsl 0
+// CHECK: adds x2, x5, #0
+ adds x2, x5, #0
+
+// CHECK: {{adds xzr,|cmn}} x5, #5
+// CHECK: {{adds xzr,|cmn}} x5, #5
+ cmn x5, #5
+ cmp x5, #-5
+// CHECK: {{subs xzr,|cmp}} x6, #4095
+// CHECK: {{subs xzr,|cmp}} x6, #4095
+ cmp x6, #4095
+ cmn x6, #-4095
+// CHECK: {{adds wzr,|cmn}} w7, #5
+// CHECK: {{adds wzr,|cmn}} w7, #5
+ cmn w7, #5
+ cmp w7, #-5
+// CHECK: {{subs wzr,|cmp}} w8, #4095
+// CHECK: {{subs wzr,|cmp}} w8, #4095
+ cmp w8, #4095
+ cmn w8, #-4095
// Add/sub (immediate)
//------------------------------------------------------------------------------
-// Out of range immediates: < 0 or more than 12 bits
- add w4, w5, #-1
+// Out of range immediates: more than 12 bits
+ add w4, w5, #-4096
add w5, w6, #0x1000
- add w4, w5, #-1, lsl #12
+ add w4, w5, #-4096, lsl #12
add w5, w6, #0x1000, lsl #12
// CHECK-ERROR: error: expected compatible register, symbol or integer in range [0, 4095]
-// CHECK-ERROR-NEXT: add w4, w5, #-1
+// CHECK-ERROR-NEXT: add w4, w5, #-4096
// CHECK-ERROR-NEXT: ^
// CHECK-ERROR-AARCH64-NEXT: error: expected compatible register, symbol or integer in range [0, 4095]
// CHECK-ERROR-AARCH64-NEXT: add w5, w6, #0x1000
// CHECK-ERROR-AARCH64-NEXT: ^
// CHECK-ERROR-NEXT: error: expected compatible register, symbol or integer in range [0, 4095]
-// CHECK-ERROR-NEXT: add w4, w5, #-1, lsl #12
+// CHECK-ERROR-NEXT: add w4, w5, #-4096, lsl #12
// CHECK-ERROR-NEXT: ^
// CHECK-ERROR-NEXT: error: expected compatible register, symbol or integer in range [0, 4095]
// CHECK-ERROR-NEXT: add w5, w6, #0x1000, lsl #12