From 9f665846964c065760de4feb77879b9df8ea52da Mon Sep 17 00:00:00 2001 From: "Arnaud A. de Grandmaison" Date: Wed, 1 Jul 2015 15:05:58 +0000 Subject: [PATCH] [AArch64] Implement add/adds/sub/subs/cmp/cmn with negative immediate aliases This patch teaches the AsmParser to accept add/adds/sub/subs/cmp/cmn with a negative immediate operand and convert them as shown: add Rd, Rn, -imm -> sub Rd, Rn, imm sub Rd, Rn, -imm -> add Rd, Rn, imm adds Rd, Rn, -imm -> subs Rd, Rn, imm subs Rd, Rn, -imm -> adds Rd, Rn, imm cmp Rn, -imm -> cmn Rn, imm cmn Rn, -imm -> cmp Rn, imm Those instructions are an alternate syntax available to assembly coders, and are needed in order to support code already compiling with some other assemblers (gas). They are documented in the "ARMv8 Instruction Set Overview", in the "Arithmetic (immediate)" section. This makes llvm-mc a programmer-friendly assembler ! This also fixes PR20978: "Assembly handling of adding negative numbers not as smart as gas". llvm-svn: 241166 --- lib/Target/AArch64/AArch64InstrFormats.td | 49 ++++++++-- lib/Target/AArch64/AArch64InstrInfo.td | 8 +- .../AArch64/AsmParser/AArch64AsmParser.cpp | 31 ++++++ test/MC/AArch64/alias-addsubimm.s | 94 +++++++++++++++++++ test/MC/AArch64/basic-a64-diagnostics.s | 10 +- 5 files changed, 177 insertions(+), 15 deletions(-) create mode 100644 test/MC/AArch64/alias-addsubimm.s diff --git a/lib/Target/AArch64/AArch64InstrFormats.td b/lib/Target/AArch64/AArch64InstrFormats.td index 2c52f340d6d..3f2e772a90c 100644 --- a/lib/Target/AArch64/AArch64InstrFormats.td +++ b/lib/Target/AArch64/AArch64InstrFormats.td @@ -614,10 +614,15 @@ def move_vec_shift : Operand { 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: @@ -631,8 +636,17 @@ class addsub_shifted_imm let MIOperandInfo = (ops i32imm, i32imm); } +class addsub_shifted_imm_neg + : Operand { + let EncoderMethod = "getAddSubImmOpValue"; + let ParserMatchClass = AddSubImmNegOperand; + let MIOperandInfo = (ops i32imm, i32imm); +} + def addsub_shifted_imm32 : addsub_shifted_imm; def addsub_shifted_imm64 : addsub_shifted_imm; +def addsub_shifted_imm32_neg : addsub_shifted_imm_neg; +def addsub_shifted_imm64_neg : addsub_shifted_imm_neg; class neg_addsub_shifted_imm : Operand, ComplexPattern { @@ -1633,7 +1647,7 @@ class AddSubRegAlias; -multiclass AddSub { let hasSideEffects = 0, isReMaterializable = 1, isAsCheapAsAMove = 1 in { // Add/Subtract immediate @@ -1686,6 +1700,14 @@ multiclass AddSub sub Rd, Rn, imm + def : InstAlias(NAME # "Wri") GPR32sp:$Rd, GPR32sp:$Rn, + addsub_shifted_imm32_neg:$imm), 0>; + def : InstAlias(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(NAME#"Wrs"), GPR32, GPR32, GPR32, 0>; @@ -1706,7 +1728,8 @@ multiclass AddSub; // UXTX #0 } -multiclass AddSubS { +multiclass AddSubS { let isCompare = 1, Defs = [NZCV] in { // Add/Subtract immediate def Wri : BaseAddSubImm { } } // Defs = [NZCV] + // Support negative immediates, e.g. adds Rd, Rn, -imm -> subs Rd, Rn, imm + def : InstAlias(NAME # "Wri") GPR32:$Rd, GPR32sp:$Rn, + addsub_shifted_imm32_neg:$imm), 0>; + def : InstAlias(NAME # "Xri") GPR64:$Rd, GPR64sp:$Rn, + addsub_shifted_imm64_neg:$imm), 0>; + // Compare aliases def : InstAlias(NAME#"Wri") WZR, GPR32sp:$src, addsub_shifted_imm32:$imm), 5>; @@ -1768,6 +1799,12 @@ multiclass AddSubS { def : InstAlias(NAME#"Xrs") XZR, GPR64:$src1, GPR64:$src2, arith_shift64:$sh), 4>; + // Support negative immediates, e.g. cmp Rn, -imm -> cmn Rn, imm + def : InstAlias(NAME#"Wri") + WZR, GPR32sp:$src, addsub_shifted_imm32_neg:$imm), 0>; + def : InstAlias(NAME#"Xri") + XZR, GPR64sp:$src, addsub_shifted_imm64_neg:$imm), 0>; + // Compare shorthands def : InstAlias(NAME#"Wrs") WZR, GPR32:$src1, GPR32:$src2, 0), 5>; diff --git a/lib/Target/AArch64/AArch64InstrInfo.td b/lib/Target/AArch64/AArch64InstrInfo.td index 653f80286b2..b73e0958df9 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.td +++ b/lib/Target/AArch64/AArch64InstrInfo.td @@ -567,8 +567,8 @@ def : InstAlias<"ngcs $dst, $src", (SBCSWr GPR32:$dst, WZR, GPR32:$src)>; 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)>; @@ -579,8 +579,8 @@ def : InstAlias<"mov $dst, $src", 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), diff --git a/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 063c053ffe8..38e8b4d9a93 100644 --- a/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -699,6 +699,25 @@ public: const MCConstantExpr *CE = cast(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(Expr); + return CE != nullptr && CE->getValue() < 0 && -CE->getValue() <= 0xfff; + } bool isCondCode() const { return Kind == k_CondCode; } bool isSIMDImmType10() const { if (!isImm()) @@ -1219,6 +1238,18 @@ public: } } + void addAddSubImmNegOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + + const MCExpr *MCE = isShiftedImm() ? getShiftedImmVal() : getImm(); + const MCConstantExpr *CE = cast(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())); diff --git a/test/MC/AArch64/alias-addsubimm.s b/test/MC/AArch64/alias-addsubimm.s new file mode 100644 index 00000000000..75e0a185572 --- /dev/null +++ b/test/MC/AArch64/alias-addsubimm.s @@ -0,0 +1,94 @@ +// 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 diff --git a/test/MC/AArch64/basic-a64-diagnostics.s b/test/MC/AArch64/basic-a64-diagnostics.s index bf7db132b44..0c2bc689663 100644 --- a/test/MC/AArch64/basic-a64-diagnostics.s +++ b/test/MC/AArch64/basic-a64-diagnostics.s @@ -75,19 +75,19 @@ // 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