X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FTarget%2FARM%2FARMAsmBackend.cpp;h=9556a7d9e326331bdfbfa7cfefa1bcd11444ba5c;hb=ae5abd595f5442767313a4c8a24008ad19323ceb;hp=c07a81606841a45a474a271dd26a85fda1673453;hpb=0c628c2617a2c25cabea6d1cb8bd13b79b3cb15a;p=oota-llvm.git diff --git a/lib/Target/ARM/ARMAsmBackend.cpp b/lib/Target/ARM/ARMAsmBackend.cpp index c07a8160684..9556a7d9e32 100644 --- a/lib/Target/ARM/ARMAsmBackend.cpp +++ b/lib/Target/ARM/ARMAsmBackend.cpp @@ -7,12 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Target/TargetAsmBackend.h" #include "ARM.h" +#include "ARMAddressingModes.h" #include "ARMFixupKinds.h" #include "llvm/ADT/Twine.h" #include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCMachObjectWriter.h" #include "llvm/MC/MCObjectFormat.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSectionELF.h" @@ -21,13 +23,58 @@ #include "llvm/Support/ELF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetAsmBackend.h" #include "llvm/Target/TargetRegistry.h" using namespace llvm; namespace { +class ARMMachObjectWriter : public MCMachObjectTargetWriter { +}; + class ARMAsmBackend : public TargetAsmBackend { + bool isThumbMode; // Currently emitting Thumb code. public: - ARMAsmBackend(const Target &T) : TargetAsmBackend() {} + ARMAsmBackend(const Target &T) : TargetAsmBackend(), isThumbMode(false) {} + + 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}, +{ "fixup_arm_branch", 0, 24, MCFixupKindInfo::FKF_IsPCRel }, +{ "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 }, +{ "fixup_arm_thumb_bcc", 1, 8, MCFixupKindInfo::FKF_IsPCRel }, +{ "fixup_arm_movt_hi16", 0, 16, 0 }, +{ "fixup_arm_movw_lo16", 0, 16, 0 }, + }; + + if (Kind < FirstTargetFixupKind) + return TargetAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; + } bool MayNeedRelaxation(const MCInst &Inst) const; @@ -35,9 +82,21 @@ public: bool WriteNopData(uint64_t Count, MCObjectWriter *OW) const; - unsigned getPointerSize() const { - return 4; + void HandleAssemblerFlag(MCAssemblerFlag Flag) { + switch (Flag) { + default: break; + case MCAF_Code16: + setIsThumb(true); + break; + case MCAF_Code32: + setIsThumb(false); + break; + } } + + unsigned getPointerSize() const { return 4; } + bool isThumb() const { return isThumbMode; } + void setIsThumb(bool it) { isThumbMode = it; } }; } // end anonymous namespace @@ -52,10 +111,19 @@ void ARMAsmBackend::RelaxInstruction(const MCInst &Inst, MCInst &Res) const { } bool ARMAsmBackend::WriteNopData(uint64_t Count, MCObjectWriter *OW) const { - // FIXME: Zero fill for now. That's not right, but at least will get the - // section size right. + if (isThumb()) { + assert (((Count & 1) == 0) && "Unaligned Nop data fragment!"); + // FIXME: 0xbf00 is the ARMv7 value. For v6 and before, we'll need to + // use 0x46c0 (which is a 'mov r8, r8' insn). + Count /= 2; + for (uint64_t i = 0; i != Count; ++i) + OW->Write16(0xbf00); + return true; + } + // ARM mode + Count /= 4; for (uint64_t i = 0; i != Count; ++i) - OW->Write8(0); + OW->Write32(0xe1a00000); return true; } @@ -64,28 +132,173 @@ static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) { default: llvm_unreachable("Unknown fixup kind!"); case FK_Data_4: + return Value; case ARM::fixup_arm_movt_hi16: - case ARM::fixup_arm_movw_lo16: + case ARM::fixup_arm_movw_lo16: { + unsigned Hi4 = (Value & 0xF000) >> 12; + unsigned Lo12 = Value & 0x0FFF; + // inst{19-16} = Hi4; + // inst{11-0} = Lo12; + Value = (Hi4 << 16) | (Lo12); return Value; - case ARM::fixup_arm_pcrel_12: { - bool isAdd = true; + } + case ARM::fixup_arm_ldst_pcrel_12: // ARM PC-relative values are offset by 8. - Value -= 8; + Value -= 4; + // FALLTHROUGH + case ARM::fixup_t2_ldst_pcrel_12: { + // Offset by 4, adjusted by two due to the half-word ordering of thumb. + Value -= 4; + bool isAdd = true; if ((int64_t)Value < 0) { Value = -Value; isAdd = false; } assert ((Value < 4096) && "Out of range pc-relative fixup value!"); Value |= isAdd << 23; + + // 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; + } + return Value; } + case ARM::fixup_thumb_adr_pcrel_10: + return ((Value - 4) >> 2) & 0xff; + 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); + } + + 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); + out |= (Value & 0x800) << 14; + out |= (Value & 0x700) << 4; + out |= (Value & 0x0FF); + + uint64_t swapped = (out & 0xFFFF0000) >> 16; + swapped |= (out & 0x0000FFFF) << 16; + return swapped; + } + case ARM::fixup_arm_branch: // These values don't encode the low two bits since they're always zero. // Offset by 8 just as above. - return (Value - 8) >> 2; - case ARM::fixup_arm_pcrel_10: { - // Offset by 8 just as above. - Value = Value - 8; + return 0xffffff & ((Value - 8) >> 2); + case ARM::fixup_t2_uncondbranch: { + Value = Value - 4; + Value >>= 1; // Low bit is not encoded. + + uint32_t out = 0; + bool I = Value & 0x800000; + bool J1 = Value & 0x400000; + bool J2 = Value & 0x200000; + J1 ^= I; + J2 ^= I; + + 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 + + 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. + + uint64_t out = 0; + 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 + + uint32_t swapped = (out & 0xFFFF0000) >> 16; + swapped |= (out & 0x0000FFFF) << 16; + return swapped; + } + 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 + // + // BL: xxxxxSIIIIIIIIII xxxxxIIIIIIIIIII + // + // Note that the halfwords are stored high first, low second; so we need + // to transpose the fixup value here to map properly. + unsigned isNeg = (int64_t(Value) < 0) ? 1 : 0; + 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. + 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. + // + // BLX: xxxxxSIIIIIIIIII xxxxxIIIIIIIIII0 + // + // Note that the halfwords are stored high first, low second; so we need + // to transpose the fixup value here to map properly. + unsigned isNeg = (int64_t(Value) < 0) ? 1 : 0; + 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. + return Binary; + } + case ARM::fixup_arm_thumb_cp: + // 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; + case ARM::fixup_arm_thumb_cb: { + // Offset by 4 and don't encode the lower bit, which is always 0. + uint32_t Binary = (Value - 4) >> 1; + return ((Binary & 0x20) << 4) | ((Binary & 0x1f) << 3); + } + 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; + 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; + case ARM::fixup_arm_pcrel_10: + Value = Value - 4; // ARM fixups offset by an additional word and don't + // 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. + Value = Value - 4; bool isAdd = true; if ((int64_t)Value < 0) { Value = -Value; @@ -95,12 +308,22 @@ static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) { Value >>= 2; assert ((Value < 256) && "Out of range pc-relative fixup value!"); Value |= isAdd << 23; + + // Same addressing mode as fixup_arm_pcrel_10, + // but with 16-bit halfwords swapped. + if (Kind == ARM::fixup_t2_pcrel_10) { + uint32_t swapped = (Value & 0xFFFF0000) >> 16; + swapped |= (Value & 0x0000FFFF) << 16; + return swapped; + } + return Value; } } } namespace { + // FIXME: This should be in a separate file. // ELF is an ELF of course... class ELFARMAsmBackend : public ARMAsmBackend { @@ -117,7 +340,7 @@ public: return Format; } - void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF, + void ApplyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize, uint64_t Value) const; MCObjectWriter *createObjectWriter(raw_ostream &OS) const { @@ -128,45 +351,23 @@ public: } }; -// Fixme: can we raise this to share code between Darwin and ELF? -void ELFARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF, - uint64_t Value) const { - uint32_t Mask = 0; - // Fixme: 2 for Thumb - unsigned NumBytes = 4; +// FIXME: Raise this to share code between Darwin and ELF. +void ELFARMAsmBackend::ApplyFixup(const MCFixup &Fixup, char *Data, + unsigned DataSize, uint64_t Value) const { + unsigned NumBytes = 4; // FIXME: 2 for Thumb Value = adjustFixupValue(Fixup.getKind(), Value); + if (!Value) return; // Doesn't change encoding. - switch (Fixup.getKind()) { - default: assert(0 && "Unsupported Fixup kind"); break; - case ARM::fixup_arm_branch: { - unsigned Lo24 = Value & 0xFFFFFF; - Mask = ~(0xFFFFFF); - Value = Lo24; - }; break; - case ARM::fixup_arm_movt_hi16: - case ARM::fixup_arm_movw_lo16: { - unsigned Hi4 = (Value & 0xF000) >> 12; - unsigned Lo12 = Value & 0x0FFF; - // inst{19-16} = Hi4; - // inst{11-0} = Lo12; - Value = (Hi4 << 16) | (Lo12); - Mask = ~(0xF0FFF); - }; break; - } + unsigned Offset = Fixup.getOffset(); + assert(Offset % NumBytes == 0 && "Offset mod NumBytes is nonzero!"); - assert((Fixup.getOffset() % NumBytes == 0) - && "Offset mod NumBytes is nonzero!"); - // 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. - // Fixme: how to share code with the .td generated code? - for (unsigned i = 0; i != NumBytes; ++i) { - DF.getContents()[Fixup.getOffset() + i] &= uint8_t(Mask >> (i * 8)); - DF.getContents()[Fixup.getOffset() + i] |= uint8_t(Value >> (i * 8)); - } + // 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); } -namespace { // FIXME: This should be in a separate file. class DarwinARMAsmBackend : public ARMAsmBackend { MCMachOObjectFormat Format; @@ -179,12 +380,13 @@ public: return Format; } - void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF, + void ApplyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize, uint64_t Value) const; MCObjectWriter *createObjectWriter(raw_ostream &OS) const { // FIXME: Subtarget info should be derived. Force v7 for now. - return createMachObjectWriter(OS, /*Is64Bit=*/false, + return createMachObjectWriter(new ARMMachObjectWriter, + OS, /*Is64Bit=*/false, object::mach::CTM_ARM, object::mach::CSARM_V7, /*IsLittleEndian=*/true); @@ -194,30 +396,55 @@ public: return false; } }; -} // end anonymous namespace +/// getFixupKindNumBytes - The number of bytes the fixup may change. static unsigned getFixupKindNumBytes(unsigned Kind) { switch (Kind) { - default: llvm_unreachable("Unknown fixup kind!"); - case FK_Data_4: return 4; - case ARM::fixup_arm_pcrel_12: return 3; - case ARM::fixup_arm_pcrel_10: return 3; - case ARM::fixup_arm_branch: return 3; + default: + llvm_unreachable("Unknown fixup kind!"); + + case ARM::fixup_arm_thumb_bcc: + case ARM::fixup_arm_thumb_cp: + case ARM::fixup_thumb_adr_pcrel_10: + return 1; + + case ARM::fixup_arm_thumb_br: + case ARM::fixup_arm_thumb_cb: + return 2; + + case ARM::fixup_arm_ldst_pcrel_12: + case ARM::fixup_arm_pcrel_10: + case ARM::fixup_arm_adr_pcrel_12: + case ARM::fixup_arm_branch: + return 3; + + case FK_Data_4: + case ARM::fixup_t2_ldst_pcrel_12: + case ARM::fixup_t2_condbranch: + case ARM::fixup_t2_uncondbranch: + case ARM::fixup_t2_pcrel_10: + case ARM::fixup_t2_adr_pcrel_12: + case ARM::fixup_arm_thumb_bl: + case ARM::fixup_arm_thumb_blx: + return 4; } } -void DarwinARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF, - uint64_t Value) const { +void DarwinARMAsmBackend::ApplyFixup(const MCFixup &Fixup, char *Data, + unsigned DataSize, uint64_t Value) const { unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind()); Value = adjustFixupValue(Fixup.getKind(), Value); + if (!Value) return; // Doesn't change encoding. + + unsigned Offset = Fixup.getOffset(); + assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!"); - assert(Fixup.getOffset() + NumBytes <= DF.getContents().size() && - "Invalid fixup offset!"); // 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) - DF.getContents()[Fixup.getOffset() + i] |= uint8_t(Value >> (i * 8)); + Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); } + } // end anonymous namespace TargetAsmBackend *llvm::createARMAsmBackend(const Target &T,