//
//===----------------------------------------------------------------------===//
-#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/MCObjectFormat.h"
#include "llvm/MC/MCObjectWriter.h"
#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 ARMAsmBackend : public TargetAsmBackend {
+ bool isThumbMode; // Currently emitting Thumb code.
public:
- ARMAsmBackend(const Target &T) : TargetAsmBackend() {}
+ ARMAsmBackend(const Target &T) : TargetAsmBackend(), isThumbMode(false) {}
bool MayNeedRelaxation(const MCInst &Inst) const;
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
}
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;
}
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_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_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) << 9) | ((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;
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 {
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: 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;
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 {
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:
+ 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_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,