Commit 9c110d55 authored by Hans Wennborg's avatar Hans Wennborg
Browse files

Merging r352929:

------------------------------------------------------------------------
r352929 | mstorsjo | 2019-02-01 23:08:09 +0100 (Fri, 01 Feb 2019) | 11 lines

[COFF] Create range extension thunks for ARM64

On ARM64, this is normally necessary only after a module exceeds
128 MB in size (while the limit for thumb is 16 MB). For conditional
branches, the range limit is only 1 MB though (the same as for thumb),
and for the tbz instruction, the range is only 32 KB, which allows for
a test much smaller than the full 128 MB.

This fixes PR40467.

Differential Revision: https://reviews.llvm.org/D57575
------------------------------------------------------------------------

llvm-svn: 353158
parent ca26c44d
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -669,18 +669,38 @@ const uint8_t ArmThunk[] = {
    0xe7, 0x44,             // L1: add  pc, ip
};

size_t RangeExtensionThunk::getSize() const {
size_t RangeExtensionThunkARM::getSize() const {
  assert(Config->Machine == ARMNT);
  return sizeof(ArmThunk);
}

void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
void RangeExtensionThunkARM::writeTo(uint8_t *Buf) const {
  assert(Config->Machine == ARMNT);
  uint64_t Offset = Target->getRVA() - RVA - 12;
  memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
  applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
}

// A position independent ARM64 adrp+add thunk, with a maximum range of
// +/- 4 GB, which is enough for any PE-COFF.
const uint8_t Arm64Thunk[] = {
    0x10, 0x00, 0x00, 0x90, // adrp x16, Dest
    0x10, 0x02, 0x00, 0x91, // add  x16, x16, :lo12:Dest
    0x00, 0x02, 0x1f, 0xd6, // br   x16
};

size_t RangeExtensionThunkARM64::getSize() const {
  assert(Config->Machine == ARM64);
  return sizeof(Arm64Thunk);
}

void RangeExtensionThunkARM64::writeTo(uint8_t *Buf) const {
  assert(Config->Machine == ARM64);
  memcpy(Buf + OutputSectionOff, Arm64Thunk, sizeof(Arm64Thunk));
  applyArm64Addr(Buf + OutputSectionOff + 0, Target->getRVA(), RVA, 12);
  applyArm64Imm(Buf + OutputSectionOff + 4, Target->getRVA() & 0xfff, 0);
}

void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
  Res->emplace_back(getRVA());
}
+11 −2
Original line number Diff line number Diff line
@@ -355,9 +355,18 @@ private:
  Defined *ImpSymbol;
};

class RangeExtensionThunk : public Chunk {
class RangeExtensionThunkARM : public Chunk {
public:
  explicit RangeExtensionThunk(Defined *T) : Target(T) {}
  explicit RangeExtensionThunkARM(Defined *T) : Target(T) {}
  size_t getSize() const override;
  void writeTo(uint8_t *Buf) const override;

  Defined *Target;
};

class RangeExtensionThunkARM64 : public Chunk {
public:
  explicit RangeExtensionThunkARM64(Defined *T) : Target(T) {}
  size_t getSize() const override;
  void writeTo(uint8_t *Buf) const override;

+37 −12
Original line number Diff line number Diff line
@@ -306,7 +306,7 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
// Check whether the target address S is in range from a relocation
// of type RelType at address P.
static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
  assert(Config->Machine == ARMNT);
  if (Config->Machine == ARMNT) {
    int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
    switch (RelType) {
    case IMAGE_REL_ARM_BRANCH20T:
@@ -317,6 +317,21 @@ static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
    default:
      return true;
    }
  } else if (Config->Machine == ARM64) {
    int64_t Diff = AbsoluteDifference(S, P) + Margin;
    switch (RelType) {
    case IMAGE_REL_ARM64_BRANCH26:
      return isInt<28>(Diff);
    case IMAGE_REL_ARM64_BRANCH19:
      return isInt<21>(Diff);
    case IMAGE_REL_ARM64_BRANCH14:
      return isInt<16>(Diff);
    default:
      return true;
    }
  } else {
    llvm_unreachable("Unexpected architecture");
  }
}

// Return the last thunk for the given target if it is in range,
@@ -327,7 +342,17 @@ getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
  Defined *&LastThunk = LastThunks[Target->getRVA()];
  if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
    return {LastThunk, false};
  RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
  Chunk *C;
  switch (Config->Machine) {
  case ARMNT:
    C = make<RangeExtensionThunkARM>(Target);
    break;
  case ARM64:
    C = make<RangeExtensionThunkARM64>(Target);
    break;
  default:
    llvm_unreachable("Unexpected architecture");
  }
  Defined *D = make<DefinedSynthetic>("", C);
  LastThunk = D;
  return {D, true};
@@ -429,7 +454,7 @@ static bool verifyRanges(const std::vector<Chunk *> Chunks) {
// Assign addresses and add thunks if necessary.
void Writer::finalizeAddresses() {
  assignAddresses();
  if (Config->Machine != ARMNT)
  if (Config->Machine != ARMNT && Config->Machine != ARM64)
    return;

  size_t OrigNumChunks = 0;
+0 −16
Original line number Diff line number Diff line
// REQUIRES: aarch64

// RUN: echo -e '.globl _start\n _start:\n bl too_far26\n' > %t.main26.s
// RUN: echo -e '.globl _start\n _start:\n b.ne too_far19\n' > %t.main19.s
// RUN: echo -e '.globl _start\n _start:\n tbz x0, #0, too_far14\n' > %t.main14.s

// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main26.s -o %t.main26.obj
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main19.s -o %t.main19.obj
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %t.main14.s -o %t.main14.obj
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/far-arm64-abs.s -o %t.far.obj

// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main26.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main19.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
// RUN: not lld-link -base:0x10000 -entry:_start -subsystem:console %t.main14.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s

// CHECK: relocation out of range
+27 −0
Original line number Diff line number Diff line
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %s -o %t.obj
// RUN: lld-link -entry:main -subsystem:console %t.obj -out:%t.exe -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
// RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DISASM %s

// VERBOSE: Added 1 thunks with margin {{.*}} in 1 passes

    .globl main
    .globl func1
    .text
main:
    tbz w0, #0, func1
    ret
    .section .text$a, "xr"
    .space 0x8000
    .section .text$b, "xr"
func1:
    ret

// DISASM: 0000000140001000 .text:
// DISASM: 140001000:      40 00 00 36     tbz     w0, #0, #8 <.text+0x8>
// DISASM: 140001004:      c0 03 5f d6     ret
// DISASM: 140001008:      50 00 00 90     adrp    x16, #32768
// DISASM: 14000100c:      10 52 00 91     add     x16, x16, #20
// DISASM: 140001010:      00 02 1f d6     br      x16

// DISASM: 140009014:      c0 03 5f d6     ret