Index: lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp =================================================================== --- lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -697,7 +697,9 @@ // Otherwise it should be a real immediate in range: const MCConstantExpr *CE = cast(Expr); - return CE->getValue() >= 0 && CE->getValue() <= 0xfff; + // Negative values are accepted as long as they can be transformed + // into a valid SUB/ADD operands. + return CE->getValue() >= -(0xfff) && CE->getValue() <= 0xfff; } bool isCondCode() const { return Kind == k_CondCode; } bool isSIMDImmType10() const { @@ -3313,6 +3315,29 @@ return false; } +// Get reverse Opcode for ADD/SUB due to negative immediates +// if it is semantically possible to swap them. +static unsigned getAddSubReverseOpp(unsigned Opcode) { + switch(Opcode) { + default: + return 0; + // FIXME: Can we swap those? + case AArch64::ADDSWri: + case AArch64::ADDSXri: + case AArch64::SUBSWri: + case AArch64::SUBSXri: + return 0; + case AArch64::ADDWri: + return AArch64::SUBWri; + case AArch64::ADDXri: + return AArch64::SUBXri; + case AArch64::SUBWri: + return AArch64::ADDWri; + case AArch64::SUBXri: + return AArch64::ADDXri; + } +} + // FIXME: This entire function is a giant hack to provide us with decent // operand range validation/diagnostics until TableGen/MC can be extended // to support autogeneration of this kind of validation. @@ -3449,6 +3474,23 @@ case AArch64::SUBXri: { // Annoyingly we can't do this in the isAddSubImm predicate, so there is // some slight duplication here. + if (Inst.getOperand(2).isImm()) { + // ADD/SUB can safely swap if their operands are negative + // ARMv8 Instruction Set Overview 5.4.1 + int Imm = Inst.getOperand(2).getImm(); + if (Imm < 0 && Imm > -4096) { + unsigned ReverseOp = getAddSubReverseOpp(Inst.getOpcode()); + if (!ReverseOp) + return Error(Loc[2], + "negative immediate only in ADD/SUB up to -4095"); + // FIXME: Better way of re-writing the instruction? + auto It = Inst.begin() + 2; + Inst.setOpcode(ReverseOp); + Inst.erase(It); + Inst.insert(It, MCOperand::createImm(-Imm)); + return false; + } + } if (Inst.getOperand(2).isExpr()) { const MCExpr *Expr = Inst.getOperand(2).getExpr(); AArch64MCExpr::VariantKind ELFRefKind; Index: test/MC/AArch64/addsub-inline-asm.ll =================================================================== --- /dev/null +++ test/MC/AArch64/addsub-inline-asm.ll @@ -0,0 +1,15 @@ +; RUN: llc %s -o - | FileCheck %s +; ModuleID = 'a64.c' +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" +target triple = "aarch64" + +define i32 @main(i32 %argc, i8** nocapture readnone %argv) { +entry: +; CHECK-LABEL: main + %0 = tail call i32 asm sideeffect "add ${0:w}, ${0:w}, ${1:w}\09\0A", "=&r,IJr,~{memory}"(i32 -1024) #1 +; CHECK: sub{{.*}}#1024 + %1 = tail call i32 asm sideeffect "sub ${0:w}, ${0:w}, ${1:w}\09\0A", "=&r,IJr,~{memory}"(i32 -4095) #1 +; CHECK: add{{.*}}#4095 + ret i32 %1 +} + Index: test/MC/AArch64/addsub.s =================================================================== --- /dev/null +++ test/MC/AArch64/addsub.s @@ -0,0 +1,44 @@ +// RUN: llvm-mc -arch aarch64 %s | FileCheck %s + + .text + .globl main + .align 2 + .type main,@function +main: + //CHECK: add w0, w0, #0 + add w0, w0, #0 + //CHECK: sub w0, w0, #1 + add w0, w0, #-1 + //CHECK: sub w0, w0, #1024 + add w0, w0, #-1024 + //CHECK: sub w0, w0, #2048 + add w0, w0, #-2048 + //CHECK: sub w0, w0, #4095 + add w0, w0, #-4095 + //CHECK: sub w4, w5, #1, lsl #12 + add w4, w5, #-1, lsl #12 + // FIXME: Add support for + // add w0, w0, #-4096 + // To translate to: + // sub w0, w0, #1, lsl #12 + // and vice-versa + + //CHECK: add w0, w0, #1, lsl #12 + add w0, w0, #4096 + + //CHECK: sub w0, w0, #0 + sub w0, w0, #0 + //CHECK: add w0, w0, #1 + sub w0, w0, #-1 + //CHECK: add w0, w0, #1024 + sub w0, w0, #-1024 + //CHECK: add w0, w0, #2048 + sub w0, w0, #-2048 + //CHECK: add w0, w0, #4095 + sub w0, w0, #-4095 + //CHECK: add w4, w5, #1, lsl #12 + sub w4, w5, #-1, lsl #12 + + ret +.Lfunc_end0: + .size main, .Lfunc_end0-main Index: test/MC/AArch64/basic-a64-diagnostics.s =================================================================== --- test/MC/AArch64/basic-a64-diagnostics.s +++ test/MC/AArch64/basic-a64-diagnostics.s @@ -75,23 +75,23 @@ // Add/sub (immediate) //------------------------------------------------------------------------------ -// Out of range immediates: < 0 or more than 12 bits - add w4, w5, #-1 - add w5, w6, #0x1000 - add w4, w5, #-1, lsl #12 +// Out of range immediates: wrong instruction to flip or more than 12 bits + add w5, w6, #0x1001 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: ^ -// 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: ^ + add w5, w6, #-4096 + adds w5, w6, #-1 // 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 w5, w6, #0x1001 // 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 // CHECK-ERROR-NEXT: ^ +// CHECK-ERROR-NEXT: error: expected compatible register, symbol or integer in range [0, 4095] +// CHECK-ERROR-NEXT: add w5, w6, #-4096 +// CHECK-ERROR-NEXT: ^ +// CHECK-ERROR-NEXT: error: negative immediate only in ADD/SUB up to -4095 +// CHECK-ERROR-NEXT: adds w5, w6, #-1 +// CHECK-ERROR-NEXT: ^ // Only lsl #0 and lsl #12 are allowed add w2, w3, #0x1, lsl #1