Skip to content

Commit

Permalink
[ARM] [Assembler] Support negative immediates for A32, T32 and T16
Browse files Browse the repository at this point in the history
Summary:
To support negative immediates for certain arithmetic instructions, the
instruction is converted to the inverse instruction with a negated (or inverted)
immediate. For example, "ADD r0, r1, #FFFFFFFF" cannot be encoded as an ADD
instruction.  However, "SUB r0, r1, #1" is equivalent.

These conversions are different from instruction aliases.  An alias maps
several assembler instructions onto one encoding.  A conversion, however, maps
an *invalid* instruction--e.g. with an immediate that cannot be represented in
the encoding--to a different (but equivalent) instruction.

Several instructions with negative immediates were being converted already, but
this was not systematically tested, nor did it cover all instructions.

This patch implements all possible substitutions for ARM, Thumb1 and
Thumb2 assembler and adds tests.  It also adds a feature flag
(-mattr=+no-neg-immediates) to turn these substitutions off.  This is
helpful for users who want their code to assemble to exactly what they
wrote.

Reviewers: t.p.northover, rovka, samparker, javed.absar, peter.smith, rengolin

Reviewed By: javed.absar

Subscribers: aadg, aemerson, llvm-commits

Differential Revision: https://reviews.llvm.org/D30571

llvm-svn: 298380
  • Loading branch information
snnw committed Mar 21, 2017
1 parent 019e1c4 commit 2409c64
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 33 deletions.
6 changes: 6 additions & 0 deletions llvm/lib/Target/ARM/ARM.td
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ def FeatureNoMovt : SubtargetFeature<"no-movt", "NoMovt", "true",
"Don't use movt/movw pairs for 32-bit "
"imms">;

def FeatureNoNegativeImmediates : SubtargetFeature<"no-neg-immediates",
"NegativeImmediates", "false",
"Convert immediates and instructions "
"to their negated or complemented "
"equivalent when the immediate does "
"not fit in the encoding.">;

//===----------------------------------------------------------------------===//
// ARM ISAa.
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/Target/ARM/ARMInstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,19 @@ def const_pool_asm_imm : Operand<i32> {
// Note: When EmitPriority == 1, the alias will be used for printing
class ARMInstAlias<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>, Requires<[IsARM]>;
class ARMInstSubst<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>,
Requires<[IsARM,UseNegativeImmediates]>;
class tInstAlias<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>, Requires<[IsThumb]>;
class tInstSubst<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>,
Requires<[IsThumb,UseNegativeImmediates]>;
class t2InstAlias<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>, Requires<[IsThumb2]>;
class t2InstSubst<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>,
Requires<[IsThumb2,UseNegativeImmediates]>;
class VFP2InstAlias<string Asm, dag Result, bit EmitPriority = 0>
: InstAlias<Asm, Result, EmitPriority>, Requires<[HasVFP2]>;
class VFP2DPInstAlias<string Asm, dag Result, bit EmitPriority = 0>
Expand Down
41 changes: 31 additions & 10 deletions llvm/lib/Target/ARM/ARMInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ def UseNaClTrap : Predicate<"Subtarget->useNaClTrap()">,
AssemblerPredicate<"FeatureNaClTrap", "NaCl">;
def DontUseNaClTrap : Predicate<"!Subtarget->useNaClTrap()">;

def UseNegativeImmediates :
Predicate<"false">,
AssemblerPredicate<"!FeatureNoNegativeImmediates",
"NegativeImmediates">;

// FIXME: Eventually this will be just "hasV6T2Ops".
def UseMovt : Predicate<"Subtarget->useMovt(*MF)">;
def DontUseMovt : Predicate<"!Subtarget->useMovt(*MF)">;
Expand Down Expand Up @@ -5790,33 +5795,49 @@ def : MnemonicAlias<"usubaddx", "usax">;

// "mov Rd, mod_imm_not" can be handled via "mvn" in assembly, just like
// for isel.
def : ARMInstAlias<"mov${s}${p} $Rd, $imm",
def : ARMInstSubst<"mov${s}${p} $Rd, $imm",
(MVNi rGPR:$Rd, mod_imm_not:$imm, pred:$p, cc_out:$s)>;
def : ARMInstAlias<"mvn${s}${p} $Rd, $imm",
def : ARMInstSubst<"mvn${s}${p} $Rd, $imm",
(MOVi rGPR:$Rd, mod_imm_not:$imm, pred:$p, cc_out:$s)>;
// Same for AND <--> BIC
def : ARMInstAlias<"bic${s}${p} $Rd, $Rn, $imm",
def : ARMInstSubst<"bic${s}${p} $Rd, $Rn, $imm",
(ANDri GPR:$Rd, GPR:$Rn, mod_imm_not:$imm,
pred:$p, cc_out:$s)>;
def : ARMInstAlias<"bic${s}${p} $Rdn, $imm",
def : ARMInstSubst<"bic${s}${p} $Rdn, $imm",
(ANDri GPR:$Rdn, GPR:$Rdn, mod_imm_not:$imm,
pred:$p, cc_out:$s)>;
def : ARMInstAlias<"and${s}${p} $Rd, $Rn, $imm",
def : ARMInstSubst<"and${s}${p} $Rd, $Rn, $imm",
(BICri GPR:$Rd, GPR:$Rn, mod_imm_not:$imm,
pred:$p, cc_out:$s)>;
def : ARMInstAlias<"and${s}${p} $Rdn, $imm",
def : ARMInstSubst<"and${s}${p} $Rdn, $imm",
(BICri GPR:$Rdn, GPR:$Rdn, mod_imm_not:$imm,
pred:$p, cc_out:$s)>;

// Likewise, "add Rd, mod_imm_neg" -> sub
def : ARMInstAlias<"add${s}${p} $Rd, $Rn, $imm",
def : ARMInstSubst<"add${s}${p} $Rd, $Rn, $imm",
(SUBri GPR:$Rd, GPR:$Rn, mod_imm_neg:$imm, pred:$p, cc_out:$s)>;
def : ARMInstAlias<"add${s}${p} $Rd, $imm",
def : ARMInstSubst<"add${s}${p} $Rd, $imm",
(SUBri GPR:$Rd, GPR:$Rd, mod_imm_neg:$imm, pred:$p, cc_out:$s)>;
// Likewise, "sub Rd, mod_imm_neg" -> add
def : ARMInstSubst<"sub${s}${p} $Rd, $Rn, $imm",
(ADDri GPR:$Rd, GPR:$Rn, mod_imm_neg:$imm, pred:$p, cc_out:$s)>;
def : ARMInstSubst<"sub${s}${p} $Rd, $imm",
(ADDri GPR:$Rd, GPR:$Rd, mod_imm_neg:$imm, pred:$p, cc_out:$s)>;


def : ARMInstSubst<"adc${s}${p} $Rd, $Rn, $imm",
(SBCri GPR:$Rd, GPR:$Rn, mod_imm_not:$imm, pred:$p, cc_out:$s)>;
def : ARMInstSubst<"adc${s}${p} $Rdn, $imm",
(SBCri GPR:$Rdn, GPR:$Rdn, mod_imm_not:$imm, pred:$p, cc_out:$s)>;
def : ARMInstSubst<"sbc${s}${p} $Rd, $Rn, $imm",
(ADCri GPR:$Rd, GPR:$Rn, mod_imm_not:$imm, pred:$p, cc_out:$s)>;
def : ARMInstSubst<"sbc${s}${p} $Rdn, $imm",
(ADCri GPR:$Rdn, GPR:$Rdn, mod_imm_not:$imm, pred:$p, cc_out:$s)>;

// Same for CMP <--> CMN via mod_imm_neg
def : ARMInstAlias<"cmp${p} $Rd, $imm",
def : ARMInstSubst<"cmp${p} $Rd, $imm",
(CMNri rGPR:$Rd, mod_imm_neg:$imm, pred:$p)>;
def : ARMInstAlias<"cmn${p} $Rd, $imm",
def : ARMInstSubst<"cmn${p} $Rd, $imm",
(CMPri rGPR:$Rd, mod_imm_neg:$imm, pred:$p)>;

// The shifter forms of the MOV instruction are aliased to the ASR, LSL,
Expand Down
35 changes: 33 additions & 2 deletions llvm/lib/Target/ARM/ARMInstrThumb.td
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ def imm0_7_neg : PatLeaf<(i32 imm), [{
return (uint32_t)-N->getZExtValue() < 8;
}], imm_neg_XFORM>;

def ThumbModImmNeg1_7AsmOperand : AsmOperandClass { let Name = "ThumbModImmNeg1_7"; }
def mod_imm1_7_neg : Operand<i32>, PatLeaf<(imm), [{
unsigned Value = -(unsigned)N->getZExtValue();
return 0 < Value && Value < 8;
}], imm_neg_XFORM> {
let ParserMatchClass = ThumbModImmNeg1_7AsmOperand;
}

def ThumbModImmNeg8_255AsmOperand : AsmOperandClass { let Name = "ThumbModImmNeg8_255"; }
def mod_imm8_255_neg : Operand<i32>, PatLeaf<(imm), [{
unsigned Value = -(unsigned)N->getZExtValue();
return 7 < Value && Value < 256;
}], imm_neg_XFORM> {
let ParserMatchClass = ThumbModImmNeg8_255AsmOperand;
}


def imm0_255_comp : PatLeaf<(i32 imm), [{
return ~((uint32_t)N->getZExtValue()) < 256;
}]>;
Expand Down Expand Up @@ -402,9 +419,9 @@ def tSUBspi : T1pIt<(outs GPRsp:$Rdn), (ins GPRsp:$Rn, t_imm0_508s4:$imm),
let DecoderMethod = "DecodeThumbAddSPImm";
}

def : tInstAlias<"add${p} sp, $imm",
def : tInstSubst<"add${p} sp, $imm",
(tSUBspi SP, t_imm0_508s4_neg:$imm, pred:$p)>;
def : tInstAlias<"add${p} sp, sp, $imm",
def : tInstSubst<"add${p} sp, sp, $imm",
(tSUBspi SP, t_imm0_508s4_neg:$imm, pred:$p)>;

// Can optionally specify SP as a three operand instruction.
Expand Down Expand Up @@ -946,6 +963,12 @@ let isAdd = 1 in {
}
}

def : tInstSubst<"sub${s}${p} $rd, $rn, $imm",
(tADDi3 tGPR:$rd, s_cc_out:$s, tGPR:$rn, mod_imm1_7_neg:$imm, pred:$p)>;
def : tInstSubst<"sub${s}${p} $rdn, $imm",
(tADDi8 tGPR:$rdn, s_cc_out:$s, mod_imm8_255_neg:$imm, pred:$p)>;


// AND register
let isCommutable = 1 in
def tAND : // A8.6.12
Expand Down Expand Up @@ -1213,6 +1236,14 @@ def tSUBi8 : // A8.6.210 T2
[(set tGPR:$Rdn, (add tGPR:$Rn, imm8_255_neg:$imm8))]>,
Sched<[WriteALU]>;

def : tInstSubst<"add${s}${p} $rd, $rn, $imm",
(tSUBi3 tGPR:$rd, s_cc_out:$s, tGPR:$rn, mod_imm1_7_neg:$imm, pred:$p)>;


def : tInstSubst<"add${s}${p} $rdn, $imm",
(tSUBi8 tGPR:$rdn, s_cc_out:$s, mod_imm8_255_neg:$imm, pred:$p)>;


// Subtract register
def tSUBrr : // A8.6.212
T1sIGenEncode<0b01101, (outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm),
Expand Down
51 changes: 32 additions & 19 deletions llvm/lib/Target/ARM/ARMInstrThumb2.td
Original file line number Diff line number Diff line change
Expand Up @@ -2079,6 +2079,19 @@ defm t2ADC : T2I_adde_sube_irs<0b1010, "adc", ARMadde, 1>;
defm t2SBC : T2I_adde_sube_irs<0b1011, "sbc", ARMsube>;
}

def : t2InstSubst<"adc${s}${p} $rd, $rn, $imm",
(t2SBCri rGPR:$rd, rGPR:$rn, t2_so_imm_not:$imm, pred:$p, s_cc_out:$s)>;
def : t2InstSubst<"sbc${s}${p} $rd, $rn, $imm",
(t2ADCri rGPR:$rd, rGPR:$rn, t2_so_imm_not:$imm, pred:$p, s_cc_out:$s)>;

def : t2InstSubst<"add${s}${p}.w $rd, $rn, $imm",
(t2SUBri GPRnopc:$rd, GPRnopc:$rn, t2_so_imm_neg:$imm, pred:$p, s_cc_out:$s)>;
def : t2InstSubst<"addw${p} $rd, $rn, $imm",
(t2SUBri12 GPRnopc:$rd, GPR:$rn, t2_so_imm_neg:$imm, pred:$p)>;
def : t2InstSubst<"sub${s}${p}.w $rd, $rn, $imm",
(t2ADDri GPRnopc:$rd, GPRnopc:$rn, t2_so_imm_neg:$imm, pred:$p, s_cc_out:$s)>;
def : t2InstSubst<"subw${p} $rd, $rn, $imm",
(t2ADDri12 GPRnopc:$rd, GPR:$rn, t2_so_imm_neg:$imm, pred:$p)>;
// RSB
defm t2RSB : T2I_rbin_irs <0b1110, "rsb", sub>;

Expand Down Expand Up @@ -4375,26 +4388,26 @@ def : t2InstAlias<"add${s}${p} $Rdn, $ShiftedRm",
pred:$p, cc_out:$s)>;

// add w/ negative immediates is just a sub.
def : t2InstAlias<"add${s}${p} $Rd, $Rn, $imm",
def : t2InstSubst<"add${s}${p} $Rd, $Rn, $imm",
(t2SUBri GPRnopc:$Rd, GPRnopc:$Rn, t2_so_imm_neg:$imm, pred:$p,
cc_out:$s)>;
def : t2InstAlias<"add${p} $Rd, $Rn, $imm",
def : t2InstSubst<"add${p} $Rd, $Rn, $imm",
(t2SUBri12 GPRnopc:$Rd, GPR:$Rn, imm0_4095_neg:$imm, pred:$p)>;
def : t2InstAlias<"add${s}${p} $Rdn, $imm",
def : t2InstSubst<"add${s}${p} $Rdn, $imm",
(t2SUBri GPRnopc:$Rdn, GPRnopc:$Rdn, t2_so_imm_neg:$imm, pred:$p,
cc_out:$s)>;
def : t2InstAlias<"add${p} $Rdn, $imm",
def : t2InstSubst<"add${p} $Rdn, $imm",
(t2SUBri12 GPRnopc:$Rdn, GPRnopc:$Rdn, imm0_4095_neg:$imm, pred:$p)>;

def : t2InstAlias<"add${s}${p}.w $Rd, $Rn, $imm",
def : t2InstSubst<"add${s}${p}.w $Rd, $Rn, $imm",
(t2SUBri GPRnopc:$Rd, GPRnopc:$Rn, t2_so_imm_neg:$imm, pred:$p,
cc_out:$s)>;
def : t2InstAlias<"addw${p} $Rd, $Rn, $imm",
def : t2InstSubst<"addw${p} $Rd, $Rn, $imm",
(t2SUBri12 GPRnopc:$Rd, GPR:$Rn, imm0_4095_neg:$imm, pred:$p)>;
def : t2InstAlias<"add${s}${p}.w $Rdn, $imm",
def : t2InstSubst<"add${s}${p}.w $Rdn, $imm",
(t2SUBri GPRnopc:$Rdn, GPRnopc:$Rdn, t2_so_imm_neg:$imm, pred:$p,
cc_out:$s)>;
def : t2InstAlias<"addw${p} $Rdn, $imm",
def : t2InstSubst<"addw${p} $Rdn, $imm",
(t2SUBri12 GPRnopc:$Rdn, GPRnopc:$Rdn, imm0_4095_neg:$imm, pred:$p)>;


Expand Down Expand Up @@ -4625,34 +4638,34 @@ def : t2InstAlias<"sxth${p} $Rd, $Rm$rot",

// "mov Rd, t2_so_imm_not" can be handled via "mvn" in assembly, just like
// for isel.
def : t2InstAlias<"mov${p} $Rd, $imm",
def : t2InstSubst<"mov${p} $Rd, $imm",
(t2MVNi rGPR:$Rd, t2_so_imm_not:$imm, pred:$p, zero_reg)>;
def : t2InstAlias<"mvn${p} $Rd, $imm",
(t2MOVi rGPR:$Rd, t2_so_imm_not:$imm, pred:$p, zero_reg)>;
def : t2InstSubst<"mvn${s}${p} $Rd, $imm",
(t2MOVi rGPR:$Rd, t2_so_imm_not:$imm, pred:$p, s_cc_out:$s)>;
// Same for AND <--> BIC
def : t2InstAlias<"bic${s}${p} $Rd, $Rn, $imm",
def : t2InstSubst<"bic${s}${p} $Rd, $Rn, $imm",
(t2ANDri rGPR:$Rd, rGPR:$Rn, t2_so_imm_not:$imm,
pred:$p, cc_out:$s)>;
def : t2InstAlias<"bic${s}${p} $Rdn, $imm",
def : t2InstSubst<"bic${s}${p} $Rdn, $imm",
(t2ANDri rGPR:$Rdn, rGPR:$Rdn, t2_so_imm_not:$imm,
pred:$p, cc_out:$s)>;
def : t2InstAlias<"and${s}${p} $Rd, $Rn, $imm",
def : t2InstSubst<"and${s}${p} $Rd, $Rn, $imm",
(t2BICri rGPR:$Rd, rGPR:$Rn, t2_so_imm_not:$imm,
pred:$p, cc_out:$s)>;
def : t2InstAlias<"and${s}${p} $Rdn, $imm",
def : t2InstSubst<"and${s}${p} $Rdn, $imm",
(t2BICri rGPR:$Rdn, rGPR:$Rdn, t2_so_imm_not:$imm,
pred:$p, cc_out:$s)>;
// Likewise, "add Rd, t2_so_imm_neg" -> sub
def : t2InstAlias<"add${s}${p} $Rd, $Rn, $imm",
def : t2InstSubst<"add${s}${p} $Rd, $Rn, $imm",
(t2SUBri GPRnopc:$Rd, GPRnopc:$Rn, t2_so_imm_neg:$imm,
pred:$p, cc_out:$s)>;
def : t2InstAlias<"add${s}${p} $Rd, $imm",
def : t2InstSubst<"add${s}${p} $Rd, $imm",
(t2SUBri GPRnopc:$Rd, GPRnopc:$Rd, t2_so_imm_neg:$imm,
pred:$p, cc_out:$s)>;
// Same for CMP <--> CMN via t2_so_imm_neg
def : t2InstAlias<"cmp${p} $Rd, $imm",
def : t2InstSubst<"cmp${p} $Rd, $imm",
(t2CMNri rGPR:$Rd, t2_so_imm_neg:$imm, pred:$p)>;
def : t2InstAlias<"cmn${p} $Rd, $imm",
def : t2InstSubst<"cmn${p} $Rd, $imm",
(t2CMPri rGPR:$Rd, t2_so_imm_neg:$imm, pred:$p)>;


Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/ARM/ARMSubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,10 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
/// UseSjLjEH - If true, the target uses SjLj exception handling (e.g. iOS).
bool UseSjLjEH = false;

/// Implicitly convert an instruction to a different one if its immediates
/// cannot be encoded. For example, ADD r0, r1, #FFFFFFFF -> SUB r0, r1, #1.
bool NegativeImmediates = true;

/// stackAlignment - The minimum alignment known to hold of the stack frame on
/// entry to the function and which must be maintained by every function.
unsigned stackAlignment = 4;
Expand Down
32 changes: 30 additions & 2 deletions llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,20 @@ class ARMOperand : public MCParsedAsmOperand {
return ARM_AM::getSOImmVal(Value) == -1 &&
ARM_AM::getSOImmVal(-Value) != -1;
}
bool isThumbModImmNeg1_7() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
int32_t Value = -(int32_t)CE->getValue();
return 0 < Value && Value < 8;
}
bool isThumbModImmNeg8_255() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
int32_t Value = -(int32_t)CE->getValue();
return 7 < Value && Value < 256;
}
bool isConstantPoolImm() const { return Kind == k_ConstantPoolImmediate; }
bool isBitfield() const { return Kind == k_BitfieldDescriptor; }
bool isPostIdxRegShifted() const { return Kind == k_PostIndexRegister; }
Expand Down Expand Up @@ -2035,6 +2049,20 @@ class ARMOperand : public MCParsedAsmOperand {
Inst.addOperand(MCOperand::createImm(Enc));
}

void addThumbModImmNeg8_255Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
uint32_t Val = -CE->getValue();
Inst.addOperand(MCOperand::createImm(Val));
}

void addThumbModImmNeg1_7Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
uint32_t Val = -CE->getValue();
Inst.addOperand(MCOperand::createImm(Val));
}

void addBitfieldOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
// Munge the lsb/width into a bitfield mask.
Expand Down Expand Up @@ -2141,15 +2169,15 @@ class ARMOperand : public MCParsedAsmOperand {
// The operand is actually a t2_so_imm, but we have its bitwise
// negation in the assembly source, so twiddle it here.
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
Inst.addOperand(MCOperand::createImm(~CE->getValue()));
Inst.addOperand(MCOperand::createImm(~(uint32_t)CE->getValue()));
}

void addT2SOImmNegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
// The operand is actually a t2_so_imm, but we have its
// negation in the assembly source, so twiddle it here.
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
Inst.addOperand(MCOperand::createImm(-CE->getValue()));
Inst.addOperand(MCOperand::createImm(-(uint32_t)CE->getValue()));
}

void addImm0_4095NegOperands(MCInst &Inst, unsigned N) const {
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/MC/ARM/negative-immediates-fail.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RUN: not llvm-mc -triple armv7 %s 2>&1| FileCheck %s

.arm

ADC r0, r1, #0xFFFFFEEE
# CHECK: error: invalid operand for instruction
ADC r0, r1, #0xABFEABFF
# CHECK: error: invalid operand for instruction
ADC r0, r1, #0xFFFFFE02
# CHECK: error: invalid operand for instruction

ADD.W r0, r0, #0xFF01FF01
# CHECK: error: invalid operand for instruction
15 changes: 15 additions & 0 deletions llvm/test/MC/ARM/negative-immediates-thumb1-fail.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# RUN: not llvm-mc -triple thumbv7 -mcpu=cortex-m0 %s 2>&1 | FileCheck %s

.thumb

ADDs r1, r0, #0xFFFFFFF5
# CHECK: error: instruction requires: arm-mode

ADDs r0, #0xFFFFFEFF
# CHECK: error: invalid operand for instruction

SUBs r1, r0, #0xFFFFFFF5
# CHECK: error: instruction requires: arm-mode

SUBs r0, #0xFFFFFEFF
# CHECK: error: invalid operand for instruction
19 changes: 19 additions & 0 deletions llvm/test/MC/ARM/negative-immediates-thumb1.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# RUN: llvm-mc -triple thumbv7 -mcpu=cortex-m0 %s -show-encoding | FileCheck %s
# RUN: not llvm-mc -triple thumbv7 -mcpu=cortex-m0 %s -show-encoding -mattr=+no-neg-immediates 2>&1 | FileCheck %s -check-prefix=CHECK-DISABLED

.thumb

ADDs r1, r0, #0xFFFFFFF9
# CHECK: subs r1, r0, #7
# CHECK-DISABLED: error: instruction requires: NegativeImmediates
ADDs r0, #0xFFFFFF01
# CHECK: subs r0, #255
# CHECK-DISABLED: error: instruction requires: NegativeImmediates

SUBs r0, #0xFFFFFF01
# CHECK: adds r0, #255
# CHECK-DISABLED: error: instruction requires: NegativeImmediates

SUBs r1, r0, #0xFFFFFFF9
# CHECK: adds r1, r0, #7
# CHECK-DISABLED: error: instruction requires: NegativeImmediates

0 comments on commit 2409c64

Please sign in to comment.