[llvm] r241166 - [AArch64] Implement add/adds/sub/subs/cmp/cmn with negative immediate aliases

Arnaud A. de Grandmaison arnaud.degrandmaison at arm.com
Wed Jul 1 08:05:59 PDT 2015


Author: aadg
Date: Wed Jul  1 10:05:58 2015
New Revision: 241166

URL: http://llvm.org/viewvc/llvm-project?rev=241166&view=rev
Log:
[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".

Added:
    llvm/trunk/test/MC/AArch64/alias-addsubimm.s
Modified:
    llvm/trunk/lib/Target/AArch64/AArch64InstrFormats.td
    llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td
    llvm/trunk/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
    llvm/trunk/test/MC/AArch64/basic-a64-diagnostics.s

Modified: llvm/trunk/lib/Target/AArch64/AArch64InstrFormats.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AArch64/AArch64InstrFormats.td?rev=241166&r1=241165&r2=241166&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AArch64/AArch64InstrFormats.td (original)
+++ llvm/trunk/lib/Target/AArch64/AArch64InstrFormats.td Wed Jul  1 10:05:58 2015
@@ -614,10 +614,15 @@ def move_vec_shift : Operand<i32> {
   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<ValueType Ty>
   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]> {
@@ -1633,7 +1647,7 @@ class AddSubRegAlias<string asm, Instruc
                 (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
@@ -1686,6 +1700,14 @@ multiclass AddSub<bit isSub, string mnem
     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>;
@@ -1706,7 +1728,8 @@ multiclass AddSub<bit isSub, string mnem
                        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,
@@ -1752,6 +1775,14 @@ multiclass AddSubS<bit isSub, string mne
   }
   } // 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>;
@@ -1768,6 +1799,12 @@ multiclass AddSubS<bit isSub, string mne
   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>;

Modified: llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td?rev=241166&r1=241165&r2=241166&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td (original)
+++ llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td Wed Jul  1 10:05:58 2015
@@ -567,8 +567,8 @@ def : InstAlias<"ngcs $dst, $src", (SBCS
 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),

Modified: llvm/trunk/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp?rev=241166&r1=241165&r2=241166&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (original)
+++ llvm/trunk/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp Wed Jul  1 10:05:58 2015
@@ -699,6 +699,25 @@ public:
     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())
@@ -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<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()));

Added: llvm/trunk/test/MC/AArch64/alias-addsubimm.s
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/MC/AArch64/alias-addsubimm.s?rev=241166&view=auto
==============================================================================
--- llvm/trunk/test/MC/AArch64/alias-addsubimm.s (added)
+++ llvm/trunk/test/MC/AArch64/alias-addsubimm.s Wed Jul  1 10:05:58 2015
@@ -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

Modified: llvm/trunk/test/MC/AArch64/basic-a64-diagnostics.s
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/MC/AArch64/basic-a64-diagnostics.s?rev=241166&r1=241165&r2=241166&view=diff
==============================================================================
--- llvm/trunk/test/MC/AArch64/basic-a64-diagnostics.s (original)
+++ llvm/trunk/test/MC/AArch64/basic-a64-diagnostics.s Wed Jul  1 10:05:58 2015
@@ -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





More information about the llvm-commits mailing list