diff options
| author | Arnaud A. de Grandmaison <arnaud.degrandmaison@arm.com> | 2015-07-01 15:05:58 +0000 |
|---|---|---|
| committer | Arnaud A. de Grandmaison <arnaud.degrandmaison@arm.com> | 2015-07-01 15:05:58 +0000 |
| commit | 650c520007776789b07250484c335623feb376c9 (patch) | |
| tree | f4fae9419d460d68f2f99d813cf4148e3ec4190f /llvm | |
| parent | 85b2815aba7136839524b61a96f397a526315da1 (diff) | |
| download | bcm5719-llvm-650c520007776789b07250484c335623feb376c9.tar.gz bcm5719-llvm-650c520007776789b07250484c335623feb376c9.zip | |
[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
Diffstat (limited to 'llvm')
| -rw-r--r-- | llvm/lib/Target/AArch64/AArch64InstrFormats.td | 49 | ||||
| -rw-r--r-- | llvm/lib/Target/AArch64/AArch64InstrInfo.td | 8 | ||||
| -rw-r--r-- | llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp | 31 | ||||
| -rw-r--r-- | llvm/test/MC/AArch64/alias-addsubimm.s | 94 | ||||
| -rw-r--r-- | llvm/test/MC/AArch64/basic-a64-diagnostics.s | 10 |
5 files changed, 177 insertions, 15 deletions
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index 2c52f340d6d..3f2e772a90c 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -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, Instruction inst, RegisterClass dstRegtype, (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 mnemonic, 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 mnemonic, 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 mnemonic, SDNode OpNode, string cmp> { } } // 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 mnemonic, SDNode OpNode, string cmp> { 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>; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 653f80286b2..b73e0958df9 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/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/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 063c053ffe8..38e8b4d9a93 100644 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -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())); diff --git a/llvm/test/MC/AArch64/alias-addsubimm.s b/llvm/test/MC/AArch64/alias-addsubimm.s new file mode 100644 index 00000000000..75e0a185572 --- /dev/null +++ b/llvm/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/llvm/test/MC/AArch64/basic-a64-diagnostics.s b/llvm/test/MC/AArch64/basic-a64-diagnostics.s index bf7db132b44..0c2bc689663 100644 --- a/llvm/test/MC/AArch64/basic-a64-diagnostics.s +++ b/llvm/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 |

