[lld] r342962 - [COFF] Add support for creating range extension thunks for ARM

Martin Storsjo via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 25 03:59:29 PDT 2018


Author: mstorsjo
Date: Tue Sep 25 03:59:29 2018
New Revision: 342962

URL: http://llvm.org/viewvc/llvm-project?rev=342962&view=rev
Log:
[COFF] Add support for creating range extension thunks for ARM

This is a feature that MS link.exe lacks; it currently errors out on
such relocations, just like lld did before.

This allows linking clang.exe for ARM - practically, any image over
16 MB will likely run into the issue.

Differential Revision: https://reviews.llvm.org/D52156

Added:
    lld/trunk/test/COFF/arm-thumb-thunks-multipass.s
    lld/trunk/test/COFF/arm-thumb-thunks.s
Removed:
    lld/trunk/test/COFF/Inputs/far-arm-thumb-abs.s
    lld/trunk/test/COFF/Inputs/far-arm-thumb-abs20.s
    lld/trunk/test/COFF/arm-thumb-branch-error.s
Modified:
    lld/trunk/COFF/Chunks.cpp
    lld/trunk/COFF/Chunks.h
    lld/trunk/COFF/MapFile.cpp
    lld/trunk/COFF/PDB.cpp
    lld/trunk/COFF/Writer.cpp
    lld/trunk/COFF/Writer.h
    lld/trunk/test/COFF/arm-thumb-branch20-error.s

Modified: lld/trunk/COFF/Chunks.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Chunks.cpp?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/COFF/Chunks.cpp (original)
+++ lld/trunk/COFF/Chunks.cpp Tue Sep 25 03:59:29 2018
@@ -44,6 +44,22 @@ SectionChunk::SectionChunk(ObjFile *F, c
   Live = !Config->DoGC || !isCOMDAT();
 }
 
+// Initialize the RelocTargets vector, to allow redirecting certain relocations
+// to a thunk instead of the actual symbol the relocation's symbol table index
+// indicates.
+void SectionChunk::readRelocTargets() {
+  assert(RelocTargets.empty());
+  RelocTargets.reserve(Relocs.size());
+  for (const coff_relocation &Rel : Relocs)
+    RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex));
+}
+
+// Reset RelocTargets to their original targets before thunks were added.
+void SectionChunk::resetRelocTargets() {
+  for (size_t I = 0, E = Relocs.size(); I < E; ++I)
+    RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex);
+}
+
 static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
 static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
 static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
@@ -309,7 +325,9 @@ void SectionChunk::writeTo(uint8_t *Buf)
 
   // Apply relocations.
   size_t InputSize = getSize();
-  for (const coff_relocation &Rel : Relocs) {
+  for (size_t I = 0, E = Relocs.size(); I < E; I++) {
+    const coff_relocation &Rel = Relocs[I];
+
     // Check for an invalid relocation offset. This check isn't perfect, because
     // we don't have the relocation size, which is only known after checking the
     // machine and relocation type. As a result, a relocation may overwrite the
@@ -321,8 +339,9 @@ void SectionChunk::writeTo(uint8_t *Buf)
 
     uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
 
-    auto *Sym =
-        dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
+    // Use the potentially remapped Symbol instead of the one that the
+    // relocation points to.
+    auto *Sym = dyn_cast_or_null<Defined>(RelocTargets[I]);
     if (!Sym) {
       if (isCodeView() || isDWARF())
         continue;
@@ -410,11 +429,14 @@ static uint8_t getBaserelType(const coff
 // fixed by the loader if load-time relocation is needed.
 // Only called when base relocation is enabled.
 void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
-  for (const coff_relocation &Rel : Relocs) {
+  for (size_t I = 0, E = Relocs.size(); I < E; I++) {
+    const coff_relocation &Rel = Relocs[I];
     uint8_t Ty = getBaserelType(Rel);
     if (Ty == IMAGE_REL_BASED_ABSOLUTE)
       continue;
-    Symbol *Target = File->getSymbol(Rel.SymbolTableIndex);
+    // Use the potentially remapped Symbol instead of the one that the
+    // relocation points to.
+    Symbol *Target = RelocTargets[I];
     if (!Target || isa<DefinedAbsolute>(Target))
       continue;
     Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
@@ -618,6 +640,25 @@ void ImportThunkChunkARM64::writeTo(uint
   applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
 }
 
+// A Thumb2, PIC, non-interworking range extension thunk.
+const uint8_t ArmThunk[] = {
+    0x40, 0xf2, 0x00, 0x0c, // P:  movw ip,:lower16:S - (P + (L1-P) + 4)
+    0xc0, 0xf2, 0x00, 0x0c, //     movt ip,:upper16:S - (P + (L1-P) + 4)
+    0xe7, 0x44,             // L1: add  pc, ip
+};
+
+size_t RangeExtensionThunk::getSize() const {
+  assert(Config->Machine == ARMNT);
+  return sizeof(ArmThunk);
+}
+
+void RangeExtensionThunk::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));
+}
+
 void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
   Res->emplace_back(getRVA());
 }
@@ -757,10 +798,13 @@ void MergeChunk::addSection(SectionChunk
 }
 
 void MergeChunk::finalizeContents() {
-  for (SectionChunk *C : Sections)
-    if (C->Live)
-      Builder.add(toStringRef(C->getContents()));
-  Builder.finalize();
+  if (!Finalized) {
+    for (SectionChunk *C : Sections)
+      if (C->Live)
+        Builder.add(toStringRef(C->getContents()));
+    Builder.finalize();
+    Finalized = true;
+  }
 
   for (SectionChunk *C : Sections) {
     if (!C->Live)

Modified: lld/trunk/COFF/Chunks.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Chunks.h?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/COFF/Chunks.h (original)
+++ lld/trunk/COFF/Chunks.h Tue Sep 25 03:59:29 2018
@@ -64,6 +64,13 @@ public:
   // before calling this function.
   virtual void writeTo(uint8_t *Buf) const {}
 
+  // Called by the writer once before assigning addresses and writing
+  // the output.
+  virtual void readRelocTargets() {}
+
+  // Called if restarting thunk addition.
+  virtual void resetRelocTargets() {}
+
   // Called by the writer after an RVA is assigned, but before calling
   // getSize().
   virtual void finalizeContents() {}
@@ -145,6 +152,8 @@ public:
 
   SectionChunk(ObjFile *File, const coff_section *Header);
   static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
+  void readRelocTargets() override;
+  void resetRelocTargets() override;
   size_t getSize() const override { return Header->SizeOfRawData; }
   ArrayRef<uint8_t> getContents() const;
   void writeTo(uint8_t *Buf) const override;
@@ -217,6 +226,10 @@ public:
   // Used by the garbage collector.
   bool Live;
 
+  // When inserting a thunk, we need to adjust a relocation to point to
+  // the thunk instead of the actual original target Symbol.
+  std::vector<Symbol *> RelocTargets;
+
 private:
   StringRef SectionName;
   std::vector<SectionChunk *> AssocChildren;
@@ -251,6 +264,7 @@ public:
 
 private:
   llvm::StringTableBuilder Builder;
+  bool Finalized = false;
 };
 
 // A chunk for common symbols. Common chunks don't have actual data.
@@ -338,6 +352,15 @@ private:
   Defined *ImpSymbol;
 };
 
+class RangeExtensionThunk : public Chunk {
+public:
+  explicit RangeExtensionThunk(Defined *T) : Target(T) {}
+  size_t getSize() const override;
+  void writeTo(uint8_t *Buf) const override;
+
+  Defined *Target;
+};
+
 // Windows-specific.
 // See comments for DefinedLocalImport class.
 class LocalImportChunk : public Chunk {

Modified: lld/trunk/COFF/MapFile.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/MapFile.cpp?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/COFF/MapFile.cpp (original)
+++ lld/trunk/COFF/MapFile.cpp Tue Sep 25 03:59:29 2018
@@ -110,7 +110,7 @@ void coff::writeMapFile(ArrayRef<OutputS
     writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
     OS << Sec->Name << '\n';
 
-    for (Chunk *C : Sec->getChunks()) {
+    for (Chunk *C : Sec->Chunks) {
       auto *SC = dyn_cast<SectionChunk>(C);
       if (!SC)
         continue;

Modified: lld/trunk/COFF/PDB.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/PDB.cpp?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/COFF/PDB.cpp (original)
+++ lld/trunk/COFF/PDB.cpp Tue Sep 25 03:59:29 2018
@@ -839,6 +839,7 @@ static ArrayRef<uint8_t> relocateDebugCh
   uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk.getSize());
   assert(DebugChunk.OutputSectionOff == 0 &&
          "debug sections should not be in output sections");
+  DebugChunk.readRelocTargets();
   DebugChunk.writeTo(Buffer);
   return makeArrayRef(Buffer, DebugChunk.getSize());
 }
@@ -1290,7 +1291,7 @@ void PDBLinker::addSections(ArrayRef<Out
   // Add section contributions. They must be ordered by ascending RVA.
   for (OutputSection *OS : OutputSections) {
     addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
-    for (Chunk *C : OS->getChunks()) {
+    for (Chunk *C : OS->Chunks) {
       pdb::SectionContrib SC =
           createSectionContrib(C, LinkerModule.getModuleIndex());
       Builder.getDbiBuilder().addSectionContrib(SC);

Modified: lld/trunk/COFF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.cpp?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.cpp (original)
+++ lld/trunk/COFF/Writer.cpp Tue Sep 25 03:59:29 2018
@@ -172,7 +172,9 @@ private:
       std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map);
   void createExportTable();
   void mergeSections();
+  void readRelocTargets();
   void assignAddresses();
+  void finalizeAddresses();
   void removeEmptySections();
   void createSymbolAndStringTable();
   void openFile(StringRef OutputPath);
@@ -299,6 +301,193 @@ void OutputSection::writeHeaderTo(uint8_
 } // namespace coff
 } // namespace lld
 
+// 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);
+  int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
+  switch (RelType) {
+  case IMAGE_REL_ARM_BRANCH20T:
+    return isInt<21>(Diff);
+  case IMAGE_REL_ARM_BRANCH24T:
+  case IMAGE_REL_ARM_BLX23T:
+    return isInt<25>(Diff);
+  default:
+    return true;
+  }
+}
+
+// Return the last thunk for the given target if it is in range,
+// or create a new one.
+static std::pair<Defined *, bool>
+getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
+         uint16_t Type, int Margin) {
+  Defined *&LastThunk = LastThunks[Target->getRVA()];
+  if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
+    return {LastThunk, false};
+  RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
+  Defined *D = make<DefinedSynthetic>("", C);
+  LastThunk = D;
+  return {D, true};
+}
+
+// This checks all relocations, and for any relocation which isn't in range
+// it adds a thunk after the section chunk that contains the relocation.
+// If the latest thunk for the specific target is in range, that is used
+// instead of creating a new thunk. All range checks are done with the
+// specified margin, to make sure that relocations that originally are in
+// range, but only barely, also get thunks - in case other added thunks makes
+// the target go out of range.
+//
+// After adding thunks, we verify that all relocations are in range (with
+// no extra margin requirements). If this failed, we restart (throwing away
+// the previously created thunks) and retry with a wider margin.
+static bool createThunks(std::vector<Chunk *> &Chunks, int Margin) {
+  bool AddressesChanged = false;
+  DenseMap<uint64_t, Defined *> LastThunks;
+  size_t ThunksSize = 0;
+  // Recheck Chunks.size() each iteration, since we can insert more
+  // elements into it.
+  for (size_t I = 0; I != Chunks.size(); ++I) {
+    SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Chunks[I]);
+    if (!SC)
+      continue;
+    size_t ThunkInsertionSpot = I + 1;
+
+    // Try to get a good enough estimate of where new thunks will be placed.
+    // Offset this by the size of the new thunks added so far, to make the
+    // estimate slightly better.
+    size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize;
+    for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
+      const coff_relocation &Rel = SC->Relocs[J];
+      Symbol *&RelocTarget = SC->RelocTargets[J];
+
+      // The estimate of the source address P should be pretty accurate,
+      // but we don't know whether the target Symbol address should be
+      // offset by ThunkSize or not (or by some of ThunksSize but not all of
+      // it), giving us some uncertainty once we have added one thunk.
+      uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize;
+
+      Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
+      if (!Sym)
+        continue;
+
+      uint64_t S = Sym->getRVA();
+
+      if (isInRange(Rel.Type, S, P, Margin))
+        continue;
+
+      // If the target isn't in range, hook it up to an existing or new
+      // thunk.
+      Defined *Thunk;
+      bool WasNew;
+      std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin);
+      if (WasNew) {
+        Chunk *ThunkChunk = Thunk->getChunk();
+        ThunkChunk->setRVA(
+            ThunkInsertionRVA); // Estimate of where it will be located.
+        Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk);
+        ThunkInsertionSpot++;
+        ThunksSize += ThunkChunk->getSize();
+        ThunkInsertionRVA += ThunkChunk->getSize();
+        AddressesChanged = true;
+      }
+      RelocTarget = Thunk;
+    }
+  }
+  return AddressesChanged;
+}
+
+// Verify that all relocations are in range, with no extra margin requirements.
+static bool verifyRanges(const std::vector<Chunk *> Chunks) {
+  for (Chunk *C : Chunks) {
+    SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C);
+    if (!SC)
+      continue;
+
+    for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
+      const coff_relocation &Rel = SC->Relocs[J];
+      Symbol *RelocTarget = SC->RelocTargets[J];
+
+      Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
+      if (!Sym)
+        continue;
+
+      uint64_t P = SC->getRVA() + Rel.VirtualAddress;
+      uint64_t S = Sym->getRVA();
+
+      if (!isInRange(Rel.Type, S, P, 0))
+        return false;
+    }
+  }
+  return true;
+}
+
+// Assign addresses and add thunks if necessary.
+void Writer::finalizeAddresses() {
+  assignAddresses();
+  if (Config->Machine != ARMNT)
+    return;
+
+  size_t OrigNumChunks = 0;
+  for (OutputSection *Sec : OutputSections) {
+    Sec->OrigChunks = Sec->Chunks;
+    OrigNumChunks += Sec->Chunks.size();
+  }
+
+  int Pass = 0;
+  int Margin = 1024 * 100;
+  while (true) {
+    // First check whether we need thunks at all, or if the previous pass of
+    // adding them turned out ok.
+    bool RangesOk = true;
+    size_t NumChunks = 0;
+    for (OutputSection *Sec : OutputSections) {
+      if (!verifyRanges(Sec->Chunks)) {
+        RangesOk = false;
+        break;
+      }
+      NumChunks += Sec->Chunks.size();
+    }
+    if (RangesOk) {
+      if (Pass > 0)
+        log("Added " + Twine(NumChunks - OrigNumChunks) + " thunks with " +
+            "margin " + Twine(Margin) + " in " + Twine(Pass) + " passes");
+      return;
+    }
+
+    if (Pass >= 10)
+      fatal("adding thunks hasn't converged after " + Twine(Pass) + " passes");
+
+    if (Pass > 0) {
+      // If the previous pass didn't work out, reset everything back to the
+      // original conditions before retrying with a wider margin. This should
+      // ideally never happen under real circumstances.
+      for (OutputSection *Sec : OutputSections) {
+        Sec->Chunks = Sec->OrigChunks;
+        for (Chunk *C : Sec->Chunks)
+          C->resetRelocTargets();
+      }
+      Margin *= 2;
+    }
+
+    // Try adding thunks everywhere where it is needed, with a margin
+    // to avoid things going out of range due to the added thunks.
+    bool AddressesChanged = false;
+    for (OutputSection *Sec : OutputSections)
+      AddressesChanged |= createThunks(Sec->Chunks, Margin);
+    // If the verification above thought we needed thunks, we should have
+    // added some.
+    assert(AddressesChanged);
+
+    // Recalculate the layout for the whole image (and verify the ranges at
+    // the start of the next round).
+    assignAddresses();
+
+    Pass++;
+  }
+}
+
 // The main function of the writer.
 void Writer::run() {
   ScopedTimer T1(CodeLayoutTimer);
@@ -309,7 +498,8 @@ void Writer::run() {
   appendImportThunks();
   createExportTable();
   mergeSections();
-  assignAddresses();
+  readRelocTargets();
+  finalizeAddresses();
   removeEmptySections();
   setSectionPermissions();
   createSymbolAndStringTable();
@@ -796,9 +986,9 @@ void Writer::createSymbolAndStringTable(
 }
 
 void Writer::mergeSections() {
-  if (!PdataSec->getChunks().empty()) {
-    FirstPdata = PdataSec->getChunks().front();
-    LastPdata = PdataSec->getChunks().back();
+  if (!PdataSec->Chunks.empty()) {
+    FirstPdata = PdataSec->Chunks.front();
+    LastPdata = PdataSec->Chunks.back();
   }
 
   for (auto &P : Config->Merge) {
@@ -826,6 +1016,13 @@ void Writer::mergeSections() {
   }
 }
 
+// Visits all sections to initialize their relocation targets.
+void Writer::readRelocTargets() {
+  for (OutputSection *Sec : OutputSections)
+    for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
+             [&](Chunk *C) { C->readRelocTargets(); });
+}
+
 // Visits all sections to assign incremental, non-overlapping RVAs and
 // file offsets.
 void Writer::assignAddresses() {
@@ -843,7 +1040,7 @@ void Writer::assignAddresses() {
       addBaserels();
     uint64_t RawSize = 0, VirtualSize = 0;
     Sec->Header.VirtualAddress = RVA;
-    for (Chunk *C : Sec->getChunks()) {
+    for (Chunk *C : Sec->Chunks) {
       VirtualSize = alignTo(VirtualSize, C->Alignment);
       C->setRVA(RVA + VirtualSize);
       C->OutputSectionOff = VirtualSize;
@@ -1315,7 +1512,7 @@ void Writer::writeSections() {
     // ADD instructions).
     if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE)
       memset(SecBuf, 0xCC, Sec->getRawSize());
-    for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(),
+    for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
              [&](Chunk *C) { C->writeTo(SecBuf); });
   }
 }
@@ -1399,12 +1596,13 @@ uint32_t Writer::getSizeOfInitializedDat
 void Writer::addBaserels() {
   if (!Config->Relocatable)
     return;
+  RelocSec->Chunks.clear();
   std::vector<Baserel> V;
   for (OutputSection *Sec : OutputSections) {
     if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
       continue;
     // Collect all locations for base relocations.
-    for (Chunk *C : Sec->getChunks())
+    for (Chunk *C : Sec->Chunks)
       C->getBaserels(&V);
     // Add the addresses to .reloc section.
     if (!V.empty())

Modified: lld/trunk/COFF/Writer.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.h?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.h (original)
+++ lld/trunk/COFF/Writer.h Tue Sep 25 03:59:29 2018
@@ -36,7 +36,6 @@ public:
   void addChunk(Chunk *C);
   void insertChunkAtStart(Chunk *C);
   void merge(OutputSection *Other);
-  ArrayRef<Chunk *> getChunks() { return Chunks; }
   void addPermissions(uint32_t C);
   void setPermissions(uint32_t C);
   uint64_t getRVA() { return Header.VirtualAddress; }
@@ -63,9 +62,11 @@ public:
   llvm::StringRef Name;
   llvm::object::coff_section Header = {};
 
+  std::vector<Chunk *> Chunks;
+  std::vector<Chunk *> OrigChunks;
+
 private:
   uint32_t StringTableOff = 0;
-  std::vector<Chunk *> Chunks;
 };
 
 }

Removed: lld/trunk/test/COFF/Inputs/far-arm-thumb-abs.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/Inputs/far-arm-thumb-abs.s?rev=342961&view=auto
==============================================================================
--- lld/trunk/test/COFF/Inputs/far-arm-thumb-abs.s (original)
+++ lld/trunk/test/COFF/Inputs/far-arm-thumb-abs.s (removed)
@@ -1,2 +0,0 @@
-.global too_far1
-too_far1 = 0x1401004

Removed: lld/trunk/test/COFF/Inputs/far-arm-thumb-abs20.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/Inputs/far-arm-thumb-abs20.s?rev=342961&view=auto
==============================================================================
--- lld/trunk/test/COFF/Inputs/far-arm-thumb-abs20.s (original)
+++ lld/trunk/test/COFF/Inputs/far-arm-thumb-abs20.s (removed)
@@ -1,2 +0,0 @@
-.global too_far20
-too_far20 = 0x501004

Removed: lld/trunk/test/COFF/arm-thumb-branch-error.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/arm-thumb-branch-error.s?rev=342961&view=auto
==============================================================================
--- lld/trunk/test/COFF/arm-thumb-branch-error.s (original)
+++ lld/trunk/test/COFF/arm-thumb-branch-error.s (removed)
@@ -1,10 +0,0 @@
-// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %s -o %t
-// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %S/Inputs/far-arm-thumb-abs.s -o %tfar
-// RUN: not lld-link -entry:_start -subsystem:console %t %tfar -out:%t2 2>&1 | FileCheck %s
-// REQUIRES: arm
- .syntax unified
- .globl _start
-_start:
- bl  too_far1
-
-// CHECK: relocation out of range

Modified: lld/trunk/test/COFF/arm-thumb-branch20-error.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/arm-thumb-branch20-error.s?rev=342962&r1=342961&r2=342962&view=diff
==============================================================================
--- lld/trunk/test/COFF/arm-thumb-branch20-error.s (original)
+++ lld/trunk/test/COFF/arm-thumb-branch20-error.s Tue Sep 25 03:59:29 2018
@@ -1,10 +1,16 @@
 // REQUIRES: arm
 // RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %s -o %t.obj
-// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %S/Inputs/far-arm-thumb-abs20.s -o %t.far.obj
-// RUN: not lld-link -entry:_start -subsystem:console %t.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
+// RUN: not lld-link -entry:_start -subsystem:console %t.obj -out:%t.exe 2>&1 | FileCheck %s
  .syntax unified
  .globl _start
 _start:
  bne too_far20
+ .space 0x100000
+ .section .text$a, "xr"
+too_far20:
+ bx lr
 
-// CHECK: relocation out of range
+// When trying to add a thunk at the end of the section, the thunk itself
+// will be too far away, so this won't converge.
+
+// CHECK: adding thunks hasn't converged

Added: lld/trunk/test/COFF/arm-thumb-thunks-multipass.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/arm-thumb-thunks-multipass.s?rev=342962&view=auto
==============================================================================
--- lld/trunk/test/COFF/arm-thumb-thunks-multipass.s (added)
+++ lld/trunk/test/COFF/arm-thumb-thunks-multipass.s Tue Sep 25 03:59:29 2018
@@ -0,0 +1,70 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7-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 -start-address=0x403000 -stop-address=0x403008 | FileCheck -check-prefix=FUNC01 %s
+// RUN: llvm-objdump -d %t.exe -start-address=0x404ffa -stop-address=0x405012 | FileCheck -check-prefix=FUNC01-THUNKS %s
+
+// VERBOSE: Added {{.*}} thunks with margin 204800 in 2 passes
+
+    .syntax unified
+    .globl main
+    .text
+main:
+    b  func01
+    bx lr
+
+.irp i, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18
+    .section .text$\i\()a, "xr"
+    .balign 8192
+func\i:
+    bne far_func\i
+    bne func_within_margin\i
+    // Originally, the first section is less than 8192 bytes large, and the
+    // second one follows almost directly. After adding one thunk after
+    // the first section, the second one will move forward by 8192 bytes
+    // due to the alignment.
+    .space 8192 - 8 - 4
+
+    .section .text$\i\()b, "xr"
+    .balign 8192
+align\i:
+    nop
+.endr
+
+    .section .text$999, "xr"
+tail:
+    .space 0x100000 - 100*1024 - 18*8192*2
+    // Initially, these symbols are within range from all the sections above,
+    // even when taking the initial margin into account. After adding thunks
+    // to all the sections above, some of these are also out of range, forcing
+    // running a second pass.
+.irp i, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18
+func_within_margin\i:
+    nop
+.endr
+    .space 0x100000
+
+    // These are always out of range.
+.irp i, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18
+far_func\i:
+    nop
+.endr
+    bx lr
+
+// FUNC01: 403000:       41 f0 fc 87     bne.w   #8184 <.text+0x3ffc>
+// FUNC01: 403004:       41 f0 ff 87     bne.w   #8190 <.text+0x4006>
+
+// Check that we only have two thunks here, even if we created the first
+// thunk twice (once in the first pass, then thrown away and recreated
+// in the second pass).
+
+// FUNC01-THUNKS: 404ffa:       00 00           movs    r0,  r0
+// The instruction above is padding from the .space
+// FUNC01-THUNKS: 404ffc:       47 f2 1e 0c     movw    r12, #28702
+// FUNC01-THUNKS: 405000:       c0 f2 20 0c     movt    r12, #32
+// FUNC01-THUNKS: 405004:       e7 44           add     pc,  r12
+// FUNC01-THUNKS: 405006:       46 f6 f0 7c     movw    r12, #28656
+// FUNC01-THUNKS: 40500a:       c0 f2 10 0c     movt    r12, #16
+// FUNC01-THUNKS: 40500e:       e7 44           add     pc, r12
+// The instruction below is padding from the .balign
+// FUNC01-THUNKS: 405010:       cc cc           ldm     r4!, {r2, r3, r6, r7}

Added: lld/trunk/test/COFF/arm-thumb-thunks.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/arm-thumb-thunks.s?rev=342962&view=auto
==============================================================================
--- lld/trunk/test/COFF/arm-thumb-thunks.s (added)
+++ lld/trunk/test/COFF/arm-thumb-thunks.s Tue Sep 25 03:59:29 2018
@@ -0,0 +1,75 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7-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 -start-address=0x401000 -stop-address=0x401022 | FileCheck -check-prefix=MAIN %s
+// RUN: llvm-objdump -d %t.exe -start-address=0x501022 -stop-address=0x501032 | FileCheck -check-prefix=FUNC1 %s
+// RUN: llvm-objdump -d %t.exe -start-address=0x601032 | FileCheck -check-prefix=FUNC2 %s
+
+// VERBOSE: Added 3 thunks with margin {{.*}} in 1 passes
+
+    .syntax unified
+    .globl main
+    .globl func1
+    .text
+main:
+    bne func1
+    bne func2
+    // This should reuse the same thunk as func1 above
+    bne func1_alias
+    bx lr
+    .section .text$a, "xr"
+    .space 0x100000
+    .section .text$b, "xr"
+func1:
+func1_alias:
+    // This shouldn't reuse the func2 thunk from above, since it is out
+    // of range.
+    bne func2
+    bx lr
+    .section .text$c, "xr"
+    .space 0x100000
+    .section .text$d, "xr"
+func2:
+// Test using string tail merging. This is irrelevant to the thunking itself,
+// but running multiple passes of assignAddresses() calls finalizeAddresses()
+// multiple times; check that MergeChunk handles this correctly.
+    movw r0, :lower16:"??_C at string1"
+    movt r0, :upper16:"??_C at string1"
+    movw r1, :lower16:"??_C at string2"
+    movt r1, :upper16:"??_C at string2"
+    bx lr
+
+    .section .rdata,"dr",discard,"??_C at string1"
+    .globl "??_C at string1"
+"??_C at string1":
+    .asciz "foobar"
+    .section .rdata,"dr",discard,"??_C at string2"
+    .globl "??_C at string2"
+"??_C at string2":
+    .asciz "bar"
+
+// MAIN:    401000:       40 f0 05 80     bne.w   #10 <.text+0xe>
+// MAIN:    401004:       40 f0 08 80     bne.w   #16 <.text+0x18>
+// MAIN:    401008:       40 f0 01 80     bne.w   #2 <.text+0xe>
+// MAIN:    40100c:       70 47           bx      lr
+// func1 thunk
+// MAIN:    40100e:       40 f2 08 0c     movw    r12, #8
+// MAIN:    401012:       c0 f2 10 0c     movt    r12, #16
+// MAIN:    401016:       e7 44           add     pc,  r12
+// func2 thunk
+// MAIN:    401018:       40 f2 0e 0c     movw    r12, #14
+// MAIN:    40101c:       c0 f2 20 0c     movt    r12, #32
+// MAIN:    401020:       e7 44           add     pc,  r12
+
+// FUNC1:   501022:       40 f0 01 80     bne.w   #2 <.text+0x100028>
+// FUNC1:   501026:       70 47           bx      lr
+// func2 thunk
+// FUNC1:   501028:       4f f6 fe 7c     movw    r12, #65534
+// FUNC1:   50102c:       c0 f2 0f 0c     movt    r12, #15
+// FUNC1:   501030:       e7 44           add     pc,  r12
+
+// FUNC2:   601032:       42 f2 00 00     movw    r0, #8192
+// FUNC2:   601036:       c0 f2 60 00     movt    r0, #96
+// FUNC2:   60103a:       42 f2 03 01     movw    r1, #8195
+// FUNC2:   60103e:       c0 f2 60 01     movt    r1, #96
+// FUNC2:   601042:       70 47   bx      lr




More information about the llvm-commits mailing list