Skip to content

Commit

Permalink
[ARM] Support fixup for Thumb2 modified immediate
Browse files Browse the repository at this point in the history
    
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

llvm-svn: 304702
  • Loading branch information
smithp35 committed Jun 5, 2017
1 parent 78819e0 commit adde667
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 5 deletions.
15 changes: 13 additions & 2 deletions llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
Expand Up @@ -1026,6 +1026,15 @@ class ARMOperand : public MCParsedAsmOperand {
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;
Expand Down Expand Up @@ -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() &&
Expand Down Expand Up @@ -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)) &&
Expand Down
20 changes: 20 additions & 0 deletions llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp
Expand Up @@ -339,7 +339,17 @@ class ARMMCCodeEmitter : public MCCodeEmitter {
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;
Expand Down
6 changes: 6 additions & 0 deletions llvm/test/MC/ARM/big-endian-thumb2-fixup.s
Expand Up @@ -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
13 changes: 13 additions & 0 deletions llvm/test/MC/ARM/t2-modified-immediate-fixup-error1.s
@@ -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
12 changes: 12 additions & 0 deletions llvm/test/MC/ARM/t2-modified-immediate-fixup-error2.s
@@ -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
45 changes: 45 additions & 0 deletions llvm/test/MC/ARM/t2-modified-immediate-fixup.s
@@ -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
2 changes: 0 additions & 2 deletions llvm/test/MC/ARM/thumb2-diagnostics.s
Expand Up @@ -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
Expand Down

0 comments on commit adde667

Please sign in to comment.