[ARM] Support fixup for Thumb2 modified immediate

This change adds a new fixup fixup_t2_so_imm for the t2_so_imm_asmoperand
"T2SOImm". The fixup permits code such as:
.L1:
 sub r3, r3, #.L2 - .L1
.L2:
to assemble in Thumb2 as well as in ARM state.
    
The operand predicate isT2SOImm() explicitly doesn't match expressions
containing :upper16: and :lower16: as expressions with these operators
must match the movt and movw instructions.
    
The test mov r0, foo2 in thumb2-diagnostics is moved to a new file as the
fixup delays the error message till after the assembler has quit due to
the other errors.
    
As the mov instruction shares the t2_so_imm_asmoperand mov instructions
with a non constant expression now match t2MOVi rather than t2MOVi16 so the
error message is slightly different.
    
Fixes PR28647

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



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@304702 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Peter Smith 2017-06-05 09:37:12 +00:00
parent 3303806a3d
commit f9779131d1
9 changed files with 123 additions and 5 deletions

View File

@ -1026,6 +1026,15 @@ public:
ARM_AM::getSOImmVal(-Value) != -1);
}
bool isT2SOImm() const {
// If we have an immediate that's not a constant, treat it as an expression
// needing a fixup.
if (isImm() && !isa<MCConstantExpr>(getImm())) {
// We want to avoid matching :upper16: and :lower16: as we want these
// expressions to match in isImm0_65535Expr()
const ARMMCExpr *ARM16Expr = dyn_cast<ARMMCExpr>(getImm());
return (!ARM16Expr || (ARM16Expr->getKind() != ARMMCExpr::VK_ARM_HI16 &&
ARM16Expr->getKind() != ARMMCExpr::VK_ARM_LO16));
}
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
@ -8404,7 +8413,8 @@ bool ARMAsmParser::processInstruction(MCInst &Inst,
// wide encoding wasn't explicit.
if (Inst.getOperand(0).getReg() != Inst.getOperand(1).getReg() ||
!isARMLowRegister(Inst.getOperand(0).getReg()) ||
(unsigned)Inst.getOperand(2).getImm() > 255 ||
(Inst.getOperand(2).isImm() &&
(unsigned)Inst.getOperand(2).getImm() > 255) ||
((!inITBlock() && Inst.getOperand(5).getReg() != ARM::CPSR) ||
(inITBlock() && Inst.getOperand(5).getReg() != 0)) ||
(static_cast<ARMOperand &>(*Operands[3]).isToken() &&
@ -8556,7 +8566,8 @@ bool ARMAsmParser::processInstruction(MCInst &Inst,
// If we can use the 16-bit encoding and the user didn't explicitly
// request the 32-bit variant, transform it here.
if (isARMLowRegister(Inst.getOperand(0).getReg()) &&
(unsigned)Inst.getOperand(1).getImm() <= 255 &&
(Inst.getOperand(1).isImm() &&
(unsigned)Inst.getOperand(1).getImm() <= 255) &&
((!inITBlock() && Inst.getOperand(2).getImm() == ARMCC::AL &&
Inst.getOperand(4).getReg() == ARM::CPSR) ||
(inITBlock() && Inst.getOperand(4).getReg() == 0)) &&

View File

@ -98,6 +98,7 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
{"fixup_t2_movt_hi16", 0, 20, 0},
{"fixup_t2_movw_lo16", 0, 20, 0},
{"fixup_arm_mod_imm", 0, 12, 0},
{"fixup_t2_so_imm", 0, 26, 0},
};
const static MCFixupKindInfo InfosBE[ARM::NumTargetFixupKinds] = {
// This table *must* be in the order that the fixup_* kinds are defined in
@ -148,6 +149,7 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
{"fixup_t2_movt_hi16", 12, 20, 0},
{"fixup_t2_movw_lo16", 12, 20, 0},
{"fixup_arm_mod_imm", 20, 12, 0},
{"fixup_t2_so_imm", 26, 6, 0},
};
if (Kind < FirstTargetFixupKind)
@ -693,6 +695,22 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
return 0;
}
return Value;
case ARM::fixup_t2_so_imm:
Value = ARM_AM::getT2SOImmVal(Value);
if ((int64_t)Value < 0) {
Ctx.reportError(Fixup.getLoc(), "out of range immediate fixup value");
return 0;
}
// Value will contain a 12-bit value broken up into a 4-bit shift in bits
// 11:8 and the 8-bit immediate in 0:7. The instruction has the immediate
// in 0:7. The 4-bit shift is split up into i:imm3 where i is placed at bit
// 10 of the upper half-word and imm3 is placed at 14:12 of the lower
// half-word.
uint64_t EncValue = 0;
EncValue |= (Value & 0x800) << 15;
EncValue |= (Value & 0x700) << 4;
EncValue |= (Value & 0xff);
return swapHalfWords(EncValue, IsLittleEndian);
}
}
@ -792,6 +810,7 @@ static unsigned getFixupKindNumBytes(unsigned Kind) {
case ARM::fixup_arm_movw_lo16:
case ARM::fixup_t2_movt_hi16:
case ARM::fixup_t2_movw_lo16:
case ARM::fixup_t2_so_imm:
return 4;
case FK_SecRel_2:
@ -844,6 +863,7 @@ static unsigned getFixupKindContainerSizeBytes(unsigned Kind) {
case ARM::fixup_t2_movt_hi16:
case ARM::fixup_t2_movw_lo16:
case ARM::fixup_arm_mod_imm:
case ARM::fixup_t2_so_imm:
// Instruction size is 4 bytes.
return 4;
}

View File

@ -110,6 +110,9 @@ enum Fixups {
// fixup_arm_mod_imm - Fixup for mod_imm
fixup_arm_mod_imm,
// fixup_t2_so_imm - Fixup for Thumb2 8-bit rotated operand
fixup_t2_so_imm,
// Marker
LastTargetFixupKind,
NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind

View File

@ -339,7 +339,17 @@ public:
unsigned getT2SOImmOpValue(const MCInst &MI, unsigned Op,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
unsigned SoImm = MI.getOperand(Op).getImm();
const MCOperand &MO = MI.getOperand(Op);
// Support for fixups (MCFixup)
if (MO.isExpr()) {
const MCExpr *Expr = MO.getExpr();
// Fixups resolve to plain values that need to be encoded.
MCFixupKind Kind = MCFixupKind(ARM::fixup_t2_so_imm);
Fixups.push_back(MCFixup::create(0, Expr, Kind, MI.getLoc()));
return 0;
}
unsigned SoImm = MO.getImm();
unsigned Encoded = ARM_AM::getT2SOImmVal(SoImm);
assert(Encoded != ~0U && "Not a Thumb2 so_imm value?");
return Encoded;

View File

@ -47,3 +47,9 @@ ldst_precel_12_label:
nop
adr_pcrel_12_label:
@ARM::fixup_t2_so_imm
.section s_t2_so_imm,"ax",%progbits
// CHECK-LABEL: Contents of section s_t2_so_imm
// CHECK: 0000 f1033337
add r3, r3,val
.equ val,0x37373737

View File

@ -0,0 +1,13 @@
@ PR28647
@ RUN: not llvm-mc -triple=thumbv7a-linux-gnueabi -filetype=obj < %s 2>&1 | FileCheck %s
.text
.syntax unified
.balign 2
@ Error with unencodeable immediate
add r1, r2, sym0
@ CHECK: error: out of range immediate fixup value
.equ sym0, 0x01abcdef
.L2:
mov r0, .L2
@ CHECK: error: unsupported relocation on symbol

View File

@ -0,0 +1,12 @@
@ PR28647
@ RUN: not llvm-mc -triple=thumbv7a-linux-gnueabi -filetype=obj < %s 2>&1 | FileCheck %s
.text
.syntax unified
.balign 2
@ mov with :upper16: or :lower16: should not match mov with modified immediate
mov r0, :upper16: sym0
@ CHECK: error: instruction requires: arm-mode
mov r0, :lower16: sym0
@ CHECK: error: instruction requires: arm-mode
.equ sym0, 0x01abcdef

View File

@ -0,0 +1,45 @@
@ PR28647
@ RUN: llvm-mc < %s -triple=thumbv7a-linux-gnueabi -filetype=obj -o - \
@ RUN: | llvm-objdump --disassemble -triple=thumbv7a-linux-gnueabi - | FileCheck %s
.text
.syntax unified
.balign 2
@ Thumb2 modified immediate instructions
add r1,r1, sym0
sub r1,r2, sym1
cmp r2, sym2
and r4,r4, sym3
orr r8,r9, sym4
teq r1, sym5
tst r1, sym6
sbc r1,r1, sym7
adc r1,r0, sym8
@CHECK: add.w r1, r1, #255
@CHECK: sub.w r1, r2, #16711935
@CHECK: cmp.w r2, #4278255360
@CHECK: and r4, r4, #303174162
@CHECK: orr r8, r9, #2852126720
@CHECK: teq.w r1, #1426063360
@CHECK: tst.w r1, #713031680
@CHECK: sbc r1, r1, #2785280
@CHECK: adc r1, r0, #340
.L1:
sub r3, r3, #.L2 - .L1
.L2:
@CHECK: sub.w r3, r3, #4
@ mov without :upper16: or :lower16: should match mov with modified immediate
mov r1, sym3
@CHECK: mov.w r1, #303174162
@ Modified immediate constants
.equ sym0, 0x000000ff
.equ sym1, 0x00ff00ff
.equ sym2, 0xff00ff00
.equ sym3, 0x12121212
.equ sym4, 0xaa000000
.equ sym5, 0x55000000
.equ sym6, 0x2a800000
.equ sym7, 0x002a8000
.equ sym8, 0x00000154

View File

@ -76,10 +76,8 @@
@ CHECK-ERRORS: error: branch target out of range
foo2:
mov r0, foo2
movw r0, foo2
movt r0, foo2
@ CHECK-ERRORS: error: instruction requires: arm-mode
@ CHECK-ERRORS: error: immediate expression for mov requires :lower16: or :upper16
@ CHECK-ERRORS: ^
@ CHECK-ERRORS: error: immediate expression for mov requires :lower16: or :upper16