2010-09-30 10:17:26 +08:00
|
|
|
//===-- ARMAsmBackend.cpp - ARM Assembler Backend -------------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-07-23 08:00:19 +08:00
|
|
|
#include "MCTargetDesc/ARMBaseInfo.h"
|
|
|
|
#include "MCTargetDesc/ARMFixupKinds.h"
|
2011-07-21 07:34:39 +08:00
|
|
|
#include "MCTargetDesc/ARMAddressingModes.h"
|
2010-09-30 10:17:26 +08:00
|
|
|
#include "llvm/ADT/Twine.h"
|
|
|
|
#include "llvm/MC/MCAssembler.h"
|
2010-12-08 09:16:55 +08:00
|
|
|
#include "llvm/MC/MCDirectives.h"
|
2010-12-18 00:59:53 +08:00
|
|
|
#include "llvm/MC/MCELFObjectWriter.h"
|
2010-09-30 10:17:26 +08:00
|
|
|
#include "llvm/MC/MCExpr.h"
|
2010-12-17 00:08:33 +08:00
|
|
|
#include "llvm/MC/MCMachObjectWriter.h"
|
2010-09-30 10:17:26 +08:00
|
|
|
#include "llvm/MC/MCObjectWriter.h"
|
|
|
|
#include "llvm/MC/MCSectionELF.h"
|
|
|
|
#include "llvm/MC/MCSectionMachO.h"
|
2011-07-23 08:45:41 +08:00
|
|
|
#include "llvm/MC/TargetAsmBackend.h"
|
2010-11-27 12:38:36 +08:00
|
|
|
#include "llvm/Object/MachOFormat.h"
|
2010-10-22 23:52:49 +08:00
|
|
|
#include "llvm/Support/ELF.h"
|
2010-09-30 10:17:26 +08:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
namespace {
|
2010-12-18 01:45:22 +08:00
|
|
|
class ARMELFObjectWriter : public MCELFObjectTargetWriter {
|
|
|
|
public:
|
2010-12-18 11:27:34 +08:00
|
|
|
ARMELFObjectWriter(Triple::OSType OSType)
|
|
|
|
: MCELFObjectTargetWriter(/*Is64Bit*/ false, OSType, ELF::EM_ARM,
|
|
|
|
/*HasRelocationAddend*/ false) {}
|
2010-12-18 01:45:22 +08:00
|
|
|
};
|
|
|
|
|
2010-09-30 10:17:26 +08:00
|
|
|
class ARMAsmBackend : public TargetAsmBackend {
|
2010-12-08 09:16:55 +08:00
|
|
|
bool isThumbMode; // Currently emitting Thumb code.
|
2010-09-30 10:17:26 +08:00
|
|
|
public:
|
2010-12-08 23:36:45 +08:00
|
|
|
ARMAsmBackend(const Target &T) : TargetAsmBackend(), isThumbMode(false) {}
|
2010-09-30 10:17:26 +08:00
|
|
|
|
2010-12-16 11:20:06 +08:00
|
|
|
unsigned getNumFixupKinds() const { return ARM::NumTargetFixupKinds; }
|
|
|
|
|
|
|
|
const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const {
|
|
|
|
const static MCFixupKindInfo Infos[ARM::NumTargetFixupKinds] = {
|
|
|
|
// This table *must* be in the order that the fixup_* kinds are defined in
|
|
|
|
// ARMFixupKinds.h.
|
|
|
|
//
|
|
|
|
// Name Offset (bits) Size (bits) Flags
|
|
|
|
{ "fixup_arm_ldst_pcrel_12", 1, 24, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_t2_ldst_pcrel_12", 0, 32, MCFixupKindInfo::FKF_IsPCRel |
|
|
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits},
|
|
|
|
{ "fixup_arm_pcrel_10", 1, 24, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_t2_pcrel_10", 0, 32, MCFixupKindInfo::FKF_IsPCRel |
|
|
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits},
|
|
|
|
{ "fixup_thumb_adr_pcrel_10",0, 8, MCFixupKindInfo::FKF_IsPCRel |
|
|
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits},
|
|
|
|
{ "fixup_arm_adr_pcrel_12", 1, 24, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_t2_adr_pcrel_12", 0, 32, MCFixupKindInfo::FKF_IsPCRel |
|
|
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits},
|
2011-02-05 03:47:15 +08:00
|
|
|
{ "fixup_arm_condbranch", 0, 24, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_uncondbranch", 0, 24, MCFixupKindInfo::FKF_IsPCRel },
|
2010-12-16 11:20:06 +08:00
|
|
|
{ "fixup_t2_condbranch", 0, 32, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_t2_uncondbranch", 0, 32, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_thumb_br", 0, 16, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_thumb_bl", 0, 32, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_thumb_blx", 7, 21, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_thumb_cb", 0, 16, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_thumb_cp", 1, 8, MCFixupKindInfo::FKF_IsPCRel },
|
2011-05-28 11:16:22 +08:00
|
|
|
{ "fixup_arm_thumb_bcc", 0, 8, MCFixupKindInfo::FKF_IsPCRel },
|
2011-01-14 10:38:49 +08:00
|
|
|
// movw / movt: 16-bits immediate but scattered into two chunks 0 - 12, 16 - 19.
|
|
|
|
{ "fixup_arm_movt_hi16", 0, 20, 0 },
|
|
|
|
{ "fixup_arm_movw_lo16", 0, 20, 0 },
|
|
|
|
{ "fixup_t2_movt_hi16", 0, 20, 0 },
|
|
|
|
{ "fixup_t2_movw_lo16", 0, 20, 0 },
|
|
|
|
{ "fixup_arm_movt_hi16_pcrel", 0, 20, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_arm_movw_lo16_pcrel", 0, 20, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_t2_movt_hi16_pcrel", 0, 20, MCFixupKindInfo::FKF_IsPCRel },
|
|
|
|
{ "fixup_t2_movw_lo16_pcrel", 0, 20, MCFixupKindInfo::FKF_IsPCRel },
|
2010-12-16 11:20:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (Kind < FirstTargetFixupKind)
|
|
|
|
return TargetAsmBackend::getFixupKindInfo(Kind);
|
|
|
|
|
|
|
|
assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() &&
|
|
|
|
"Invalid kind!");
|
|
|
|
return Infos[Kind - FirstTargetFixupKind];
|
|
|
|
}
|
|
|
|
|
2010-09-30 10:17:26 +08:00
|
|
|
bool MayNeedRelaxation(const MCInst &Inst) const;
|
|
|
|
|
|
|
|
void RelaxInstruction(const MCInst &Inst, MCInst &Res) const;
|
|
|
|
|
|
|
|
bool WriteNopData(uint64_t Count, MCObjectWriter *OW) const;
|
2010-10-01 01:45:51 +08:00
|
|
|
|
2010-12-08 09:16:55 +08:00
|
|
|
void HandleAssemblerFlag(MCAssemblerFlag Flag) {
|
|
|
|
switch (Flag) {
|
|
|
|
default: break;
|
|
|
|
case MCAF_Code16:
|
|
|
|
setIsThumb(true);
|
|
|
|
break;
|
|
|
|
case MCAF_Code32:
|
|
|
|
setIsThumb(false);
|
|
|
|
break;
|
|
|
|
}
|
2010-10-01 01:45:51 +08:00
|
|
|
}
|
2010-12-08 09:16:55 +08:00
|
|
|
|
|
|
|
unsigned getPointerSize() const { return 4; }
|
|
|
|
bool isThumb() const { return isThumbMode; }
|
|
|
|
void setIsThumb(bool it) { isThumbMode = it; }
|
2010-09-30 10:17:26 +08:00
|
|
|
};
|
2010-11-17 13:41:32 +08:00
|
|
|
} // end anonymous namespace
|
2010-09-30 10:17:26 +08:00
|
|
|
|
|
|
|
bool ARMAsmBackend::MayNeedRelaxation(const MCInst &Inst) const {
|
|
|
|
// FIXME: Thumb targets, different move constant targets..
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ARMAsmBackend::RelaxInstruction(const MCInst &Inst, MCInst &Res) const {
|
|
|
|
assert(0 && "ARMAsmBackend::RelaxInstruction() unimplemented");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ARMAsmBackend::WriteNopData(uint64_t Count, MCObjectWriter *OW) const {
|
2010-12-08 09:16:55 +08:00
|
|
|
if (isThumb()) {
|
|
|
|
// FIXME: 0xbf00 is the ARMv7 value. For v6 and before, we'll need to
|
|
|
|
// use 0x46c0 (which is a 'mov r8, r8' insn).
|
2010-12-18 03:03:02 +08:00
|
|
|
uint64_t NumNops = Count / 2;
|
|
|
|
for (uint64_t i = 0; i != NumNops; ++i)
|
2010-12-08 09:16:55 +08:00
|
|
|
OW->Write16(0xbf00);
|
2010-12-18 03:03:02 +08:00
|
|
|
if (Count & 1)
|
|
|
|
OW->Write8(0);
|
2010-12-08 09:16:55 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ARM mode
|
2010-12-18 03:03:02 +08:00
|
|
|
uint64_t NumNops = Count / 4;
|
|
|
|
for (uint64_t i = 0; i != NumNops; ++i)
|
2010-12-08 09:16:55 +08:00
|
|
|
OW->Write32(0xe1a00000);
|
2010-12-18 03:03:02 +08:00
|
|
|
switch (Count % 4) {
|
|
|
|
default: break; // No leftover bytes to write
|
|
|
|
case 1: OW->Write8(0); break;
|
|
|
|
case 2: OW->Write16(0); break;
|
|
|
|
case 3: OW->Write16(0); OW->Write8(0xa0); break;
|
|
|
|
}
|
|
|
|
|
2010-10-26 01:50:35 +08:00
|
|
|
return true;
|
2010-09-30 11:20:34 +08:00
|
|
|
}
|
2010-09-30 10:17:26 +08:00
|
|
|
|
2010-12-02 06:46:50 +08:00
|
|
|
static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) {
|
|
|
|
switch (Kind) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Unknown fixup kind!");
|
2010-12-18 02:39:10 +08:00
|
|
|
case FK_Data_1:
|
|
|
|
case FK_Data_2:
|
2010-12-02 06:46:50 +08:00
|
|
|
case FK_Data_4:
|
2010-12-04 03:40:23 +08:00
|
|
|
return Value;
|
2010-12-02 06:46:50 +08:00
|
|
|
case ARM::fixup_arm_movt_hi16:
|
2011-01-14 10:38:49 +08:00
|
|
|
Value >>= 16;
|
|
|
|
// Fallthrough
|
|
|
|
case ARM::fixup_arm_movw_lo16:
|
This fixes one divergence between LLVM and binutils for ARM in the
text section.
Assume the following bit of annotated assembly:
.section .data.rel.ro,"aw",%progbits
.align 2
.LAlpha:
.long startval(GOTOFF)
.text
.align 2
.type main,%function
.align 4
main: ;;; assume "main" starts at offset 0x20
0x0 push {r11, lr}
0x4 movw r0, :lower16:(.LAlpha-(.LBeta+8))
;;; ==> (.AddrOf(.LAlpha) - ((.AddrOf(.LBeta) - .AddrOf(".")) + 8)
;;; ==> (??? - ((16-4) + 8) = -20
0x8 movt r0, :upper16:(.LAlpha-(.LBeta+8))
;;; ==> (.AddrOf(.LAlpha) - ((.AddrOf(.LBeta) - .AddrOf(".")) + 8)
;;; ==> (??? - ((16-8) + 8) = -16
0xc ... blah
.LBeta:
0x10 add r0, pc, r0
0x14 ... blah
.LGamma:
0x18 add r1, pc, r1
Above snippet results in the following relocs in the .o file for the
first pair of movw/movt instructions
00000024 R_ARM_MOVW_PREL_NC .LAlpha
00000028 R_ARM_MOVT_PREL .LAlpha
And the encoded instructions in the .o file for main: must be
00000020 <main>:
20: e92d4800 push {fp, lr}
24: e30f0fec movw r0, #65516 ; 0xffec i.e. -20
28: e34f0ff0 movt r0, #65520 ; 0xfff0 i.e. -16
However, llc (prior to this commit) generates the following sequence
00000020 <main>:
20: e92d4800 push {fp, lr}
24: e30f0fec movw r0, #65516 ; 0xffec - i.e. -20
28: e34f0fff movt r0, #65535 ; 0xffff - i.e. -1
What has to happen in the ArmAsmBackend is that if the relocation is PC
relative, the 16 bits encoded as part of movw and movt must be both addends,
not addresses. It makes sense to encode addresses by right shifting the value
by 16, but the result is incorrect for PIC.
i.e., the right shift by 16 for movt is ONLY valid for the NON-PCRel case.
This change agrees with what GNU as does, and makes the PIC code run.
MC/ARM/elf-movt.s covers this case.
llvm-svn: 131674
2011-05-20 04:55:25 +08:00
|
|
|
case ARM::fixup_arm_movt_hi16_pcrel:
|
2011-01-12 08:19:25 +08:00
|
|
|
case ARM::fixup_arm_movw_lo16_pcrel: {
|
2010-12-04 03:40:23 +08:00
|
|
|
unsigned Hi4 = (Value & 0xF000) >> 12;
|
|
|
|
unsigned Lo12 = Value & 0x0FFF;
|
This fixes one divergence between LLVM and binutils for ARM in the
text section.
Assume the following bit of annotated assembly:
.section .data.rel.ro,"aw",%progbits
.align 2
.LAlpha:
.long startval(GOTOFF)
.text
.align 2
.type main,%function
.align 4
main: ;;; assume "main" starts at offset 0x20
0x0 push {r11, lr}
0x4 movw r0, :lower16:(.LAlpha-(.LBeta+8))
;;; ==> (.AddrOf(.LAlpha) - ((.AddrOf(.LBeta) - .AddrOf(".")) + 8)
;;; ==> (??? - ((16-4) + 8) = -20
0x8 movt r0, :upper16:(.LAlpha-(.LBeta+8))
;;; ==> (.AddrOf(.LAlpha) - ((.AddrOf(.LBeta) - .AddrOf(".")) + 8)
;;; ==> (??? - ((16-8) + 8) = -16
0xc ... blah
.LBeta:
0x10 add r0, pc, r0
0x14 ... blah
.LGamma:
0x18 add r1, pc, r1
Above snippet results in the following relocs in the .o file for the
first pair of movw/movt instructions
00000024 R_ARM_MOVW_PREL_NC .LAlpha
00000028 R_ARM_MOVT_PREL .LAlpha
And the encoded instructions in the .o file for main: must be
00000020 <main>:
20: e92d4800 push {fp, lr}
24: e30f0fec movw r0, #65516 ; 0xffec i.e. -20
28: e34f0ff0 movt r0, #65520 ; 0xfff0 i.e. -16
However, llc (prior to this commit) generates the following sequence
00000020 <main>:
20: e92d4800 push {fp, lr}
24: e30f0fec movw r0, #65516 ; 0xffec - i.e. -20
28: e34f0fff movt r0, #65535 ; 0xffff - i.e. -1
What has to happen in the ArmAsmBackend is that if the relocation is PC
relative, the 16 bits encoded as part of movw and movt must be both addends,
not addresses. It makes sense to encode addresses by right shifting the value
by 16, but the result is incorrect for PIC.
i.e., the right shift by 16 for movt is ONLY valid for the NON-PCRel case.
This change agrees with what GNU as does, and makes the PIC code run.
MC/ARM/elf-movt.s covers this case.
llvm-svn: 131674
2011-05-20 04:55:25 +08:00
|
|
|
assert ((((int64_t)Value) >= -0x8000) && (((int64_t)Value) <= 0x7fff) &&
|
|
|
|
"Out of range pc-relative fixup value!");
|
2010-12-04 03:40:23 +08:00
|
|
|
// inst{19-16} = Hi4;
|
|
|
|
// inst{11-0} = Lo12;
|
|
|
|
Value = (Hi4 << 16) | (Lo12);
|
2010-12-02 06:46:50 +08:00
|
|
|
return Value;
|
2010-12-04 03:40:23 +08:00
|
|
|
}
|
2011-01-14 10:38:49 +08:00
|
|
|
case ARM::fixup_t2_movt_hi16:
|
|
|
|
Value >>= 16;
|
|
|
|
// Fallthrough
|
|
|
|
case ARM::fixup_t2_movw_lo16:
|
2011-06-25 04:06:59 +08:00
|
|
|
case ARM::fixup_t2_movt_hi16_pcrel: //FIXME: Shouldn't this be shifted like
|
|
|
|
// the other hi16 fixup?
|
2011-01-14 10:38:49 +08:00
|
|
|
case ARM::fixup_t2_movw_lo16_pcrel: {
|
|
|
|
unsigned Hi4 = (Value & 0xF000) >> 12;
|
|
|
|
unsigned i = (Value & 0x800) >> 11;
|
|
|
|
unsigned Mid3 = (Value & 0x700) >> 8;
|
|
|
|
unsigned Lo8 = Value & 0x0FF;
|
|
|
|
// inst{19-16} = Hi4;
|
|
|
|
// inst{26} = i;
|
|
|
|
// inst{14-12} = Mid3;
|
|
|
|
// inst{7-0} = Lo8;
|
2011-06-25 04:06:59 +08:00
|
|
|
// The value comes in as the whole thing, not just the portion required
|
|
|
|
// for this fixup, so we need to mask off the bits not handled by this
|
|
|
|
// portion (lo vs. hi).
|
|
|
|
Value &= 0xffff;
|
2011-01-14 10:38:49 +08:00
|
|
|
Value = (Hi4 << 16) | (i << 26) | (Mid3 << 12) | (Lo8);
|
|
|
|
uint64_t swapped = (Value & 0xFFFF0000) >> 16;
|
|
|
|
swapped |= (Value & 0x0000FFFF) << 16;
|
|
|
|
return swapped;
|
|
|
|
}
|
2010-12-09 09:51:07 +08:00
|
|
|
case ARM::fixup_arm_ldst_pcrel_12:
|
2010-12-02 06:46:50 +08:00
|
|
|
// ARM PC-relative values are offset by 8.
|
2010-12-10 04:27:52 +08:00
|
|
|
Value -= 4;
|
2010-12-10 05:34:47 +08:00
|
|
|
// FALLTHROUGH
|
2010-12-09 09:51:07 +08:00
|
|
|
case ARM::fixup_t2_ldst_pcrel_12: {
|
|
|
|
// Offset by 4, adjusted by two due to the half-word ordering of thumb.
|
2010-12-10 04:27:52 +08:00
|
|
|
Value -= 4;
|
2010-12-09 09:51:07 +08:00
|
|
|
bool isAdd = true;
|
2010-12-02 06:46:50 +08:00
|
|
|
if ((int64_t)Value < 0) {
|
|
|
|
Value = -Value;
|
|
|
|
isAdd = false;
|
|
|
|
}
|
|
|
|
assert ((Value < 4096) && "Out of range pc-relative fixup value!");
|
|
|
|
Value |= isAdd << 23;
|
2010-12-14 03:18:13 +08:00
|
|
|
|
2010-12-09 09:51:07 +08:00
|
|
|
// Same addressing mode as fixup_arm_pcrel_10,
|
|
|
|
// but with 16-bit halfwords swapped.
|
|
|
|
if (Kind == ARM::fixup_t2_ldst_pcrel_12) {
|
|
|
|
uint64_t swapped = (Value & 0xFFFF0000) >> 16;
|
|
|
|
swapped |= (Value & 0x0000FFFF) << 16;
|
|
|
|
return swapped;
|
|
|
|
}
|
2010-12-14 03:18:13 +08:00
|
|
|
|
2010-12-02 06:46:50 +08:00
|
|
|
return Value;
|
|
|
|
}
|
2010-12-15 06:28:03 +08:00
|
|
|
case ARM::fixup_thumb_adr_pcrel_10:
|
|
|
|
return ((Value - 4) >> 2) & 0xff;
|
2010-12-02 08:28:45 +08:00
|
|
|
case ARM::fixup_arm_adr_pcrel_12: {
|
|
|
|
// ARM PC-relative values are offset by 8.
|
|
|
|
Value -= 8;
|
|
|
|
unsigned opc = 4; // bits {24-21}. Default to add: 0b0100
|
|
|
|
if ((int64_t)Value < 0) {
|
|
|
|
Value = -Value;
|
|
|
|
opc = 2; // 0b0010
|
|
|
|
}
|
|
|
|
assert(ARM_AM::getSOImmVal(Value) != -1 &&
|
|
|
|
"Out of range pc-relative fixup value!");
|
|
|
|
// Encode the immediate and shift the opcode into place.
|
|
|
|
return ARM_AM::getSOImmVal(Value) | (opc << 21);
|
|
|
|
}
|
2010-12-15 00:25:15 +08:00
|
|
|
|
2010-12-14 08:36:49 +08:00
|
|
|
case ARM::fixup_t2_adr_pcrel_12: {
|
|
|
|
Value -= 4;
|
|
|
|
unsigned opc = 0;
|
|
|
|
if ((int64_t)Value < 0) {
|
|
|
|
Value = -Value;
|
|
|
|
opc = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t out = (opc << 21);
|
2011-03-24 06:03:44 +08:00
|
|
|
out |= (Value & 0x800) << 15;
|
2010-12-14 08:36:49 +08:00
|
|
|
out |= (Value & 0x700) << 4;
|
|
|
|
out |= (Value & 0x0FF);
|
2010-12-15 00:25:15 +08:00
|
|
|
|
2010-12-14 08:36:49 +08:00
|
|
|
uint64_t swapped = (out & 0xFFFF0000) >> 16;
|
|
|
|
swapped |= (out & 0x0000FFFF) << 16;
|
|
|
|
return swapped;
|
|
|
|
}
|
2010-12-15 00:25:15 +08:00
|
|
|
|
2011-02-05 03:47:15 +08:00
|
|
|
case ARM::fixup_arm_condbranch:
|
|
|
|
case ARM::fixup_arm_uncondbranch:
|
2010-12-02 06:46:50 +08:00
|
|
|
// These values don't encode the low two bits since they're always zero.
|
|
|
|
// Offset by 8 just as above.
|
2010-12-07 07:57:07 +08:00
|
|
|
return 0xffffff & ((Value - 8) >> 2);
|
2010-12-14 03:31:11 +08:00
|
|
|
case ARM::fixup_t2_uncondbranch: {
|
2010-12-11 07:02:28 +08:00
|
|
|
Value = Value - 4;
|
2010-12-09 08:27:41 +08:00
|
|
|
Value >>= 1; // Low bit is not encoded.
|
2010-12-14 03:18:13 +08:00
|
|
|
|
2010-12-14 03:25:46 +08:00
|
|
|
uint32_t out = 0;
|
2010-12-14 03:31:11 +08:00
|
|
|
bool I = Value & 0x800000;
|
|
|
|
bool J1 = Value & 0x400000;
|
|
|
|
bool J2 = Value & 0x200000;
|
|
|
|
J1 ^= I;
|
|
|
|
J2 ^= I;
|
2010-12-15 00:25:15 +08:00
|
|
|
|
2010-12-14 03:31:11 +08:00
|
|
|
out |= I << 26; // S bit
|
|
|
|
out |= !J1 << 13; // J1 bit
|
|
|
|
out |= !J2 << 11; // J2 bit
|
|
|
|
out |= (Value & 0x1FF800) << 5; // imm6 field
|
|
|
|
out |= (Value & 0x0007FF); // imm11 field
|
2010-12-15 00:25:15 +08:00
|
|
|
|
2010-12-14 03:31:11 +08:00
|
|
|
uint64_t swapped = (out & 0xFFFF0000) >> 16;
|
|
|
|
swapped |= (out & 0x0000FFFF) << 16;
|
|
|
|
return swapped;
|
|
|
|
}
|
|
|
|
case ARM::fixup_t2_condbranch: {
|
|
|
|
Value = Value - 4;
|
|
|
|
Value >>= 1; // Low bit is not encoded.
|
2010-12-15 00:25:15 +08:00
|
|
|
|
2010-12-14 03:31:11 +08:00
|
|
|
uint64_t out = 0;
|
2010-12-09 09:02:09 +08:00
|
|
|
out |= (Value & 0x80000) << 7; // S bit
|
|
|
|
out |= (Value & 0x40000) >> 7; // J2 bit
|
|
|
|
out |= (Value & 0x20000) >> 4; // J1 bit
|
|
|
|
out |= (Value & 0x1F800) << 5; // imm6 field
|
|
|
|
out |= (Value & 0x007FF); // imm11 field
|
2010-12-14 03:18:13 +08:00
|
|
|
|
2010-12-14 03:25:46 +08:00
|
|
|
uint32_t swapped = (out & 0xFFFF0000) >> 16;
|
2010-12-09 08:27:41 +08:00
|
|
|
swapped |= (out & 0x0000FFFF) << 16;
|
|
|
|
return swapped;
|
|
|
|
}
|
2010-12-07 07:57:07 +08:00
|
|
|
case ARM::fixup_arm_thumb_bl: {
|
|
|
|
// The value doesn't encode the low bit (always zero) and is offset by
|
|
|
|
// four. The value is encoded into disjoint bit positions in the destination
|
|
|
|
// opcode. x = unchanged, I = immediate value bit, S = sign extension bit
|
2010-12-14 03:18:13 +08:00
|
|
|
//
|
2010-12-09 08:39:08 +08:00
|
|
|
// BL: xxxxxSIIIIIIIIII xxxxxIIIIIIIIIII
|
2010-12-14 03:18:13 +08:00
|
|
|
//
|
2010-12-07 07:57:07 +08:00
|
|
|
// Note that the halfwords are stored high first, low second; so we need
|
|
|
|
// to transpose the fixup value here to map properly.
|
2011-05-21 04:01:01 +08:00
|
|
|
unsigned isNeg = (int64_t(Value - 4) < 0) ? 1 : 0;
|
2010-12-09 08:44:33 +08:00
|
|
|
uint32_t Binary = 0;
|
|
|
|
Value = 0x3fffff & ((Value - 4) >> 1);
|
|
|
|
Binary = (Value & 0x7ff) << 16; // Low imm11 value.
|
|
|
|
Binary |= (Value & 0x1ffc00) >> 11; // High imm10 value.
|
|
|
|
Binary |= isNeg << 10; // Sign bit.
|
2010-12-09 08:39:08 +08:00
|
|
|
return Binary;
|
|
|
|
}
|
|
|
|
case ARM::fixup_arm_thumb_blx: {
|
|
|
|
// The value doesn't encode the low two bits (always zero) and is offset by
|
|
|
|
// four (see fixup_arm_thumb_cp). The value is encoded into disjoint bit
|
|
|
|
// positions in the destination opcode. x = unchanged, I = immediate value
|
|
|
|
// bit, S = sign extension bit, 0 = zero.
|
2010-12-14 03:18:13 +08:00
|
|
|
//
|
2010-12-09 08:39:08 +08:00
|
|
|
// BLX: xxxxxSIIIIIIIIII xxxxxIIIIIIIIII0
|
2010-12-14 03:18:13 +08:00
|
|
|
//
|
2010-12-09 08:39:08 +08:00
|
|
|
// Note that the halfwords are stored high first, low second; so we need
|
|
|
|
// to transpose the fixup value here to map properly.
|
2011-05-21 04:01:01 +08:00
|
|
|
unsigned isNeg = (int64_t(Value-4) < 0) ? 1 : 0;
|
2010-12-09 08:44:33 +08:00
|
|
|
uint32_t Binary = 0;
|
|
|
|
Value = 0xfffff & ((Value - 2) >> 2);
|
|
|
|
Binary = (Value & 0x3ff) << 17; // Low imm10L value.
|
|
|
|
Binary |= (Value & 0xffc00) >> 10; // High imm10H value.
|
|
|
|
Binary |= isNeg << 10; // Sign bit.
|
2010-12-07 07:57:07 +08:00
|
|
|
return Binary;
|
|
|
|
}
|
2010-12-08 09:57:09 +08:00
|
|
|
case ARM::fixup_arm_thumb_cp:
|
2010-12-09 04:32:07 +08:00
|
|
|
// Offset by 4, and don't encode the low two bits. Two bytes of that
|
|
|
|
// 'off by 4' is implicitly handled by the half-word ordering of the
|
|
|
|
// Thumb encoding, so we only need to adjust by 2 here.
|
|
|
|
return ((Value - 2) >> 2) & 0xff;
|
2010-12-10 03:50:12 +08:00
|
|
|
case ARM::fixup_arm_thumb_cb: {
|
2010-12-09 07:01:43 +08:00
|
|
|
// Offset by 4 and don't encode the lower bit, which is always 0.
|
|
|
|
uint32_t Binary = (Value - 4) >> 1;
|
2010-12-15 03:42:53 +08:00
|
|
|
return ((Binary & 0x20) << 4) | ((Binary & 0x1f) << 3);
|
2010-12-09 07:01:43 +08:00
|
|
|
}
|
2010-12-11 02:21:33 +08:00
|
|
|
case ARM::fixup_arm_thumb_br:
|
|
|
|
// Offset by 4 and don't encode the lower bit, which is always 0.
|
|
|
|
return ((Value - 4) >> 1) & 0x7ff;
|
2010-12-11 01:13:40 +08:00
|
|
|
case ARM::fixup_arm_thumb_bcc:
|
|
|
|
// Offset by 4 and don't encode the lower bit, which is always 0.
|
|
|
|
return ((Value - 4) >> 1) & 0xff;
|
2010-12-09 04:32:07 +08:00
|
|
|
case ARM::fixup_arm_pcrel_10:
|
2010-12-11 06:46:47 +08:00
|
|
|
Value = Value - 4; // ARM fixups offset by an additional word and don't
|
2010-12-09 04:32:07 +08:00
|
|
|
// need to adjust for the half-word ordering.
|
|
|
|
// Fall through.
|
|
|
|
case ARM::fixup_t2_pcrel_10: {
|
|
|
|
// Offset by 4, adjusted by two due to the half-word ordering of thumb.
|
2010-12-11 06:46:47 +08:00
|
|
|
Value = Value - 4;
|
2010-12-02 06:46:50 +08:00
|
|
|
bool isAdd = true;
|
|
|
|
if ((int64_t)Value < 0) {
|
|
|
|
Value = -Value;
|
|
|
|
isAdd = false;
|
|
|
|
}
|
|
|
|
// These values don't encode the low two bits since they're always zero.
|
|
|
|
Value >>= 2;
|
|
|
|
assert ((Value < 256) && "Out of range pc-relative fixup value!");
|
|
|
|
Value |= isAdd << 23;
|
2010-12-09 04:32:07 +08:00
|
|
|
|
2010-12-09 03:31:11 +08:00
|
|
|
// Same addressing mode as fixup_arm_pcrel_10,
|
|
|
|
// but with 16-bit halfwords swapped.
|
2010-12-08 08:18:36 +08:00
|
|
|
if (Kind == ARM::fixup_t2_pcrel_10) {
|
2010-12-14 03:25:46 +08:00
|
|
|
uint32_t swapped = (Value & 0xFFFF0000) >> 16;
|
2010-12-08 08:21:33 +08:00
|
|
|
swapped |= (Value & 0x0000FFFF) << 16;
|
2010-12-08 08:18:36 +08:00
|
|
|
return swapped;
|
|
|
|
}
|
2010-12-09 04:32:07 +08:00
|
|
|
|
2010-12-02 06:46:50 +08:00
|
|
|
return Value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-30 10:17:26 +08:00
|
|
|
namespace {
|
2010-12-08 07:05:20 +08:00
|
|
|
|
2010-09-30 10:17:26 +08:00
|
|
|
// FIXME: This should be in a separate file.
|
|
|
|
// ELF is an ELF of course...
|
|
|
|
class ELFARMAsmBackend : public ARMAsmBackend {
|
|
|
|
public:
|
|
|
|
Triple::OSType OSType;
|
|
|
|
ELFARMAsmBackend(const Target &T, Triple::OSType _OSType)
|
2010-12-17 10:06:08 +08:00
|
|
|
: ARMAsmBackend(T), OSType(_OSType) { }
|
2010-09-30 10:17:26 +08:00
|
|
|
|
2010-12-07 03:08:48 +08:00
|
|
|
void ApplyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
|
2010-09-30 10:17:26 +08:00
|
|
|
uint64_t Value) const;
|
|
|
|
|
|
|
|
MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
|
2010-12-18 11:27:34 +08:00
|
|
|
return createELFObjectWriter(new ARMELFObjectWriter(OSType), OS,
|
|
|
|
/*IsLittleEndian*/ true);
|
2010-09-30 10:17:26 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-12-08 07:05:20 +08:00
|
|
|
// FIXME: Raise this to share code between Darwin and ELF.
|
2010-12-07 03:08:48 +08:00
|
|
|
void ELFARMAsmBackend::ApplyFixup(const MCFixup &Fixup, char *Data,
|
|
|
|
unsigned DataSize, uint64_t Value) const {
|
2010-12-08 07:05:20 +08:00
|
|
|
unsigned NumBytes = 4; // FIXME: 2 for Thumb
|
2010-12-01 10:40:06 +08:00
|
|
|
Value = adjustFixupValue(Fixup.getKind(), Value);
|
2010-12-08 07:11:00 +08:00
|
|
|
if (!Value) return; // Doesn't change encoding.
|
2010-12-01 10:40:06 +08:00
|
|
|
|
2010-12-08 07:05:20 +08:00
|
|
|
unsigned Offset = Fixup.getOffset();
|
|
|
|
|
|
|
|
// For each byte of the fragment that the fixup touches, mask in the bits from
|
|
|
|
// the fixup value. The Value has been "split up" into the appropriate
|
|
|
|
// bitfields above.
|
|
|
|
for (unsigned i = 0; i != NumBytes; ++i)
|
|
|
|
Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
|
2010-09-30 10:17:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: This should be in a separate file.
|
|
|
|
class DarwinARMAsmBackend : public ARMAsmBackend {
|
|
|
|
public:
|
2011-04-02 05:07:39 +08:00
|
|
|
const object::mach::CPUSubtypeARM Subtype;
|
|
|
|
DarwinARMAsmBackend(const Target &T, object::mach::CPUSubtypeARM st)
|
|
|
|
: ARMAsmBackend(T), Subtype(st) { }
|
2010-09-30 10:17:26 +08:00
|
|
|
|
|
|
|
MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
|
2011-06-23 04:14:52 +08:00
|
|
|
return createARMMachObjectWriter(OS, /*Is64Bit=*/false,
|
|
|
|
object::mach::CTM_ARM,
|
|
|
|
Subtype);
|
2010-09-30 10:17:26 +08:00
|
|
|
}
|
|
|
|
|
2011-04-02 05:07:39 +08:00
|
|
|
void ApplyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
|
|
|
|
uint64_t Value) const;
|
|
|
|
|
2010-09-30 10:17:26 +08:00
|
|
|
virtual bool doesSectionRequireSymbols(const MCSection &Section) const {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-12-08 07:11:00 +08:00
|
|
|
/// getFixupKindNumBytes - The number of bytes the fixup may change.
|
2010-11-12 02:04:49 +08:00
|
|
|
static unsigned getFixupKindNumBytes(unsigned Kind) {
|
2010-11-09 09:37:15 +08:00
|
|
|
switch (Kind) {
|
2010-12-07 07:57:07 +08:00
|
|
|
default:
|
|
|
|
llvm_unreachable("Unknown fixup kind!");
|
2010-12-08 09:57:09 +08:00
|
|
|
|
2010-12-18 02:39:10 +08:00
|
|
|
case FK_Data_1:
|
2010-12-11 01:13:40 +08:00
|
|
|
case ARM::fixup_arm_thumb_bcc:
|
2010-12-08 09:57:09 +08:00
|
|
|
case ARM::fixup_arm_thumb_cp:
|
2010-12-15 06:28:03 +08:00
|
|
|
case ARM::fixup_thumb_adr_pcrel_10:
|
2010-12-08 09:57:09 +08:00
|
|
|
return 1;
|
|
|
|
|
2010-12-18 02:39:10 +08:00
|
|
|
case FK_Data_2:
|
2010-12-11 02:21:33 +08:00
|
|
|
case ARM::fixup_arm_thumb_br:
|
2010-12-10 03:50:12 +08:00
|
|
|
case ARM::fixup_arm_thumb_cb:
|
2010-12-09 07:01:43 +08:00
|
|
|
return 2;
|
|
|
|
|
2010-12-07 07:57:07 +08:00
|
|
|
case ARM::fixup_arm_ldst_pcrel_12:
|
|
|
|
case ARM::fixup_arm_pcrel_10:
|
|
|
|
case ARM::fixup_arm_adr_pcrel_12:
|
2011-02-05 03:47:15 +08:00
|
|
|
case ARM::fixup_arm_condbranch:
|
|
|
|
case ARM::fixup_arm_uncondbranch:
|
2010-12-07 07:57:07 +08:00
|
|
|
return 3;
|
2010-12-08 09:57:09 +08:00
|
|
|
|
|
|
|
case FK_Data_4:
|
2010-12-09 09:51:07 +08:00
|
|
|
case ARM::fixup_t2_ldst_pcrel_12:
|
2010-12-14 03:31:11 +08:00
|
|
|
case ARM::fixup_t2_condbranch:
|
|
|
|
case ARM::fixup_t2_uncondbranch:
|
2010-12-08 08:18:36 +08:00
|
|
|
case ARM::fixup_t2_pcrel_10:
|
2010-12-14 08:36:49 +08:00
|
|
|
case ARM::fixup_t2_adr_pcrel_12:
|
2010-12-07 07:57:07 +08:00
|
|
|
case ARM::fixup_arm_thumb_bl:
|
2010-12-09 08:39:08 +08:00
|
|
|
case ARM::fixup_arm_thumb_blx:
|
2011-01-14 10:38:49 +08:00
|
|
|
case ARM::fixup_arm_movt_hi16:
|
|
|
|
case ARM::fixup_arm_movw_lo16:
|
|
|
|
case ARM::fixup_arm_movt_hi16_pcrel:
|
|
|
|
case ARM::fixup_arm_movw_lo16_pcrel:
|
|
|
|
case ARM::fixup_t2_movt_hi16:
|
|
|
|
case ARM::fixup_t2_movw_lo16:
|
|
|
|
case ARM::fixup_t2_movt_hi16_pcrel:
|
|
|
|
case ARM::fixup_t2_movw_lo16_pcrel:
|
2010-12-07 07:57:07 +08:00
|
|
|
return 4;
|
2010-11-09 09:37:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-07 03:08:48 +08:00
|
|
|
void DarwinARMAsmBackend::ApplyFixup(const MCFixup &Fixup, char *Data,
|
|
|
|
unsigned DataSize, uint64_t Value) const {
|
2010-11-12 02:04:49 +08:00
|
|
|
unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
|
2010-11-09 09:37:15 +08:00
|
|
|
Value = adjustFixupValue(Fixup.getKind(), Value);
|
2010-12-08 07:11:00 +08:00
|
|
|
if (!Value) return; // Doesn't change encoding.
|
|
|
|
|
|
|
|
unsigned Offset = Fixup.getOffset();
|
|
|
|
assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");
|
2010-11-09 09:37:15 +08:00
|
|
|
|
|
|
|
// For each byte of the fragment that the fixup touches, mask in the
|
|
|
|
// bits from the fixup value.
|
|
|
|
for (unsigned i = 0; i != NumBytes; ++i)
|
2010-12-08 07:11:00 +08:00
|
|
|
Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
|
2010-09-30 10:17:26 +08:00
|
|
|
}
|
2010-12-08 07:05:20 +08:00
|
|
|
|
2010-09-30 11:21:00 +08:00
|
|
|
} // end anonymous namespace
|
2010-09-30 10:17:26 +08:00
|
|
|
|
|
|
|
TargetAsmBackend *llvm::createARMAsmBackend(const Target &T,
|
|
|
|
const std::string &TT) {
|
2011-04-02 05:07:39 +08:00
|
|
|
Triple TheTriple(TT);
|
2011-04-20 05:14:45 +08:00
|
|
|
|
|
|
|
if (TheTriple.isOSDarwin()) {
|
2011-06-15 02:08:33 +08:00
|
|
|
if (TheTriple.getArchName() == "armv4t" ||
|
|
|
|
TheTriple.getArchName() == "thumbv4t")
|
|
|
|
return new DarwinARMAsmBackend(T, object::mach::CSARM_V4T);
|
|
|
|
else if (TheTriple.getArchName() == "armv5e" ||
|
|
|
|
TheTriple.getArchName() == "thumbv5e")
|
|
|
|
return new DarwinARMAsmBackend(T, object::mach::CSARM_V5TEJ);
|
|
|
|
else if (TheTriple.getArchName() == "armv6" ||
|
2011-04-02 05:07:39 +08:00
|
|
|
TheTriple.getArchName() == "thumbv6")
|
|
|
|
return new DarwinARMAsmBackend(T, object::mach::CSARM_V6);
|
|
|
|
return new DarwinARMAsmBackend(T, object::mach::CSARM_V7);
|
|
|
|
}
|
2011-04-20 05:14:45 +08:00
|
|
|
|
|
|
|
if (TheTriple.isOSWindows())
|
2010-09-30 10:17:26 +08:00
|
|
|
assert(0 && "Windows not supported on ARM");
|
2011-04-20 05:14:45 +08:00
|
|
|
|
|
|
|
return new ELFARMAsmBackend(T, Triple(TT).getOS());
|
2010-09-30 10:17:26 +08:00
|
|
|
}
|