MachO: Add linker-optimisation hint framework to MC.
authorTim Northover <tnorthover@apple.com>
Sat, 29 Mar 2014 07:34:53 +0000 (07:34 +0000)
committerTim Northover <tnorthover@apple.com>
Sat, 29 Mar 2014 07:34:53 +0000 (07:34 +0000)
Another part of the ARM64 backend (so tests will be following soon).
This is currently used by the linker to relax adrp/ldr pairs into nops
where possible, though could well be more broadly applicable.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@205084 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/MC/MCAssembler.h
include/llvm/MC/MCLinkerOptimizationHint.h [new file with mode: 0644]
include/llvm/MC/MCStreamer.h
include/llvm/Support/MachO.h
lib/MC/CMakeLists.txt
lib/MC/MCAsmStreamer.cpp
lib/MC/MCAssembler.cpp
lib/MC/MCLinkerOptimizationHint.cpp [new file with mode: 0644]
lib/MC/MCMachOStreamer.cpp
lib/MC/MachObjectWriter.cpp

index 6cb6fd232a3bc459a38c91c89f8db455ad8cc47b..34b760c46ee69d14a5ccc375ff713b745e1bb931 100644 (file)
@@ -18,6 +18,7 @@
 #include "llvm/MC/MCDirectives.h"
 #include "llvm/MC/MCFixup.h"
 #include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCLinkerOptimizationHint.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/DataTypes.h"
@@ -932,6 +933,10 @@ private:
   // which flags to be set.
   unsigned ELFHeaderEFlags;
 
+  /// Used to communicate Linker Optimization Hint information between
+  /// the Streamer and the .o writer
+  MCLOHContainer LOHContainer;
+
   VersionMinInfoType VersionMinInfo;
 private:
   /// Evaluate a fixup to a relocatable expression and the value which should be
@@ -1174,6 +1179,19 @@ public:
 
   size_t data_region_size() const { return DataRegions.size(); }
 
+  /// @}
+  /// @name Data Region List Access
+  /// @{
+
+  // FIXME: This is a total hack, this should not be here. Once things are
+  // factored so that the streamer has direct access to the .o writer, it can
+  // disappear.
+  MCLOHContainer & getLOHContainer() {
+    return LOHContainer;
+  }
+  const MCLOHContainer & getLOHContainer() const {
+    return const_cast<MCAssembler *>(this)->getLOHContainer();
+  }
   /// @}
   /// @name Backend Data Access
   /// @{
diff --git a/include/llvm/MC/MCLinkerOptimizationHint.h b/include/llvm/MC/MCLinkerOptimizationHint.h
new file mode 100644 (file)
index 0000000..eb37d15
--- /dev/null
@@ -0,0 +1,214 @@
+//===- MCLinkerOptimizationHint.h - LOH interface ---------------*- C++ -*-===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares some helpers classes to handle Linker Optimization Hint
+// (LOH).
+//
+// FIXME: LOH interface supports only MachO format at the moment.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_MC_MCLINKEROPTIMIZATIONHINT_H
+#define LLVM_MC_MCLINKEROPTIMIZATIONHINT_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/MC/MCMachObjectWriter.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+
+// Forward declarations.
+class MCAsmLayout;
+class MCSymbol;
+
+/// Linker Optimization Hint Type.
+enum MCLOHType {
+  MCLOH_AdrpAdrp = 0x1u,      ///< Adrp xY, _v1@PAGE -> Adrp xY, _v2@PAGE.
+  MCLOH_AdrpLdr = 0x2u,       ///< Adrp _v@PAGE -> Ldr _v@PAGEOFF.
+  MCLOH_AdrpAddLdr = 0x3u,    ///< Adrp _v@PAGE -> Add _v@PAGEOFF -> Ldr.
+  MCLOH_AdrpLdrGotLdr = 0x4u, ///< Adrp _v@GOTPAGE -> Ldr _v@GOTPAGEOFF -> Ldr.
+  MCLOH_AdrpAddStr = 0x5u,    ///< Adrp _v@PAGE -> Add _v@PAGEOFF -> Str.
+  MCLOH_AdrpLdrGotStr = 0x6u, ///< Adrp _v@GOTPAGE -> Ldr _v@GOTPAGEOFF -> Str.
+  MCLOH_AdrpAdd = 0x7u,       ///< Adrp _v@PAGE -> Add _v@PAGEOFF.
+  MCLOH_AdrpLdrGot = 0x8u     ///< Adrp _v@GOTPAGE -> Ldr _v@GOTPAGEOFF.
+};
+
+static inline StringRef MCLOHDirectiveName() {
+  return StringRef(".loh");
+}
+
+static inline bool isValidMCLOHType(MCLOHType Kind) {
+  return Kind >= MCLOH_AdrpAdrp && Kind <= MCLOH_AdrpLdrGot;
+}
+
+static inline int MCLOHNameToId(StringRef Name) {
+#define MCLOHCaseNameToId(Name)     .Case(#Name, MCLOH_ ## Name)
+  return StringSwitch<int>(Name)
+    MCLOHCaseNameToId(AdrpAdrp)
+    MCLOHCaseNameToId(AdrpLdr)
+    MCLOHCaseNameToId(AdrpAddLdr)
+    MCLOHCaseNameToId(AdrpLdrGotLdr)
+    MCLOHCaseNameToId(AdrpAddStr)
+    MCLOHCaseNameToId(AdrpLdrGotStr)
+    MCLOHCaseNameToId(AdrpAdd)
+    MCLOHCaseNameToId(AdrpLdrGot)
+    .Default(-1);
+}
+
+static inline StringRef MCLOHIdToName(MCLOHType Kind) {
+#define MCLOHCaseIdToName(Name)      case MCLOH_ ## Name: return StringRef(#Name);
+  switch (Kind) {
+    MCLOHCaseIdToName(AdrpAdrp);
+    MCLOHCaseIdToName(AdrpLdr);
+    MCLOHCaseIdToName(AdrpAddLdr);
+    MCLOHCaseIdToName(AdrpLdrGotLdr);
+    MCLOHCaseIdToName(AdrpAddStr);
+    MCLOHCaseIdToName(AdrpLdrGotStr);
+    MCLOHCaseIdToName(AdrpAdd);
+    MCLOHCaseIdToName(AdrpLdrGot);
+  }
+  return StringRef();
+}
+
+static inline int MCLOHIdToNbArgs(MCLOHType Kind) {
+  switch (Kind) {
+    // LOH with two arguments
+  case MCLOH_AdrpAdrp:
+  case MCLOH_AdrpLdr:
+  case MCLOH_AdrpAdd:
+  case MCLOH_AdrpLdrGot:
+    return 2;
+    // LOH with three arguments
+  case MCLOH_AdrpAddLdr:
+  case MCLOH_AdrpLdrGotLdr:
+  case MCLOH_AdrpAddStr:
+  case MCLOH_AdrpLdrGotStr:
+    return 3;
+  }
+  return -1;
+}
+
+/// Store Linker Optimization Hint information (LOH).
+template<typename T>
+class LOHDirective {
+private:
+  MCLOHType Kind;
+
+  /// Arguments of this directive. Order matters.
+  SmallVector<T *, 3> Args;
+
+  /// Emit this directive in @p OutStream using the information available
+  /// in the given @p ObjWriter and @p Layout to get the address of the
+  /// arguments within the object file.
+  /// This function is currently specialized for T = MCSymbol.
+  void Emit_impl(raw_ostream &OutStream, const MachObjectWriter &ObjWriter,
+                 const MCAsmLayout &Layout) const {  }
+
+public:
+  typedef SmallVectorImpl<T *> LOHArgs;
+
+  LOHDirective(MCLOHType Kind, const LOHArgs &Args)
+  : Kind(Kind), Args(Args.begin(), Args.end()) {};
+
+  MCLOHType getKind() const { return Kind; }
+
+  const LOHArgs & getArgs() const { return Args; }
+
+  /// Emit this directive as:
+  /// <kind, numArgs, addr1, ..., addrN>
+  void Emit(MachObjectWriter &ObjWriter, const MCAsmLayout &Layout) const {
+    raw_ostream &OutStream = ObjWriter.getStream();
+    Emit_impl(OutStream, ObjWriter, Layout);
+  }
+
+  /// Get the size in bytes of this directive if emitted in @p ObjWriter with
+  /// the given @p Layout.
+  uint64_t getEmitSize(const MachObjectWriter &ObjWriter,
+                       const MCAsmLayout &Layout) const {
+    std::string Buffer;
+    raw_string_ostream OutStream(Buffer);
+    Emit_impl(OutStream, ObjWriter, Layout);
+    return OutStream.tell();
+  }
+};
+
+template<typename T>
+class LOHContainer {
+public:
+  typedef SmallVectorImpl<LOHDirective<T> > LOHDirectives;
+
+private:
+  /// Keep track of the emit size of all the LOHs.
+  mutable uint64_t EmitSize;
+
+  /// Keep track of all LOH directives.
+  SmallVector<LOHDirective<T>, 32> Directives;
+
+  /// Accessor to the directives.
+  LOHDirectives &getDirectives() { return Directives; }
+
+public:
+  LOHContainer() : EmitSize(0) {};
+
+  /// Const accessor to the directives.
+  const LOHDirectives &getDirectives() const {
+    return const_cast<LOHContainer *>(this)->getDirectives();
+  }
+
+  /// Add the directive of the given kind @p Kind with the given arguments
+  /// @p Args to the container.
+  void addDirective(MCLOHType Kind,
+                    const typename LOHDirective<T>::LOHArgs &Args) {
+    assert(isValidMCLOHType(Kind) && "Invalid LOH directive type!");
+    getDirectives().push_back(LOHDirective<T>(Kind, Args));
+  }
+
+  /// Get the size of the directives if emitted.
+  uint64_t getEmitSize(const MachObjectWriter &ObjWriter,
+                       const MCAsmLayout &Layout) const {
+    if (!EmitSize) {
+      for (typename LOHDirectives::const_iterator It = getDirectives().begin(),
+             EndIt = getDirectives().end(); It != EndIt; ++It)
+        EmitSize += It->getEmitSize(ObjWriter, Layout);
+    }
+    return EmitSize;
+  }
+
+  /// Emit all Linker Optimization Hint in one big table.
+  /// Each line of the table is emitted by LOHDirective::Emit.
+  void Emit(MachObjectWriter &ObjWriter, const MCAsmLayout &Layout) const {
+    for (typename LOHDirectives::const_iterator It = getDirectives().begin(),
+           EndIt = getDirectives().end(); It != EndIt; ++It)
+      It->Emit(ObjWriter, Layout);
+  }
+
+  void reset() {
+    getDirectives().clear();
+    EmitSize = 0;
+  }
+};
+
+// Add types for specialized template using MCSymbol.
+typedef LOHDirective<MCSymbol> MCLOHDirective;
+typedef LOHDirective<MCSymbol>::LOHArgs MCLOHArgs;
+
+typedef LOHContainer<MCLOHDirective>::LOHDirectives MCLOHDirectives;
+
+typedef LOHContainer<MCSymbol> MCLOHContainer;
+
+// Declare the specialization for MCSymbol.
+template<>
+void MCLOHDirective::Emit_impl(raw_ostream &OutStream,
+                               const MachObjectWriter &ObjWriter,
+                               const MCAsmLayout &Layout) const;
+} // end namespace llvm
+
+#endif
index 9fe6da96b65702f2653bbc6830c028d7de0b9fc4..33437bda80ecf8207f802891ca1acd1e7cf56b07 100644 (file)
@@ -19,6 +19,7 @@
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCDirectives.h"
 #include "llvm/MC/MCDwarf.h"
+#include "llvm/MC/MCLinkerOptimizationHint.h"
 #include "llvm/MC/MCWin64EH.h"
 #include "llvm/Support/DataTypes.h"
 #include <string>
@@ -466,6 +467,10 @@ public:
   ///
   virtual void EmitELFSize(MCSymbol *Symbol, const MCExpr *Value) = 0;
 
+  /// \brief Emit a Linker Optimization Hint (LOH) directive.
+  /// \param Args - Arguments of the LOH.
+  virtual void EmitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) {}
+
   /// EmitCommonSymbol - Emit a common symbol.
   ///
   /// @param Symbol - The common symbol to emit.
index 568d354fb56ea9ae2e3386b8bc90c5af72f02bcf..48cdc378e918437c102dbc6a8d81982e295437e2 100644 (file)
@@ -128,7 +128,8 @@ namespace llvm {
       LC_SOURCE_VERSION       = 0x0000002Au,
       LC_DYLIB_CODE_SIGN_DRS  = 0x0000002Bu,
       //                        0x0000002Cu,
-      LC_LINKER_OPTIONS       = 0x0000002Du
+      LC_LINKER_OPTIONS       = 0x0000002Du,
+      LC_LINKER_OPTIMIZATION_HINT = 0x0000002Eu
     };
 
     enum : uint32_t {
index eb3f9e52d73b358115e2735e5930fa3d108af3cb..ab7dabc9c744e07b597a767499277e0e0a56e11d 100644 (file)
@@ -24,6 +24,7 @@ add_llvm_library(LLVMMC
   MCInstPrinter.cpp
   MCInstrAnalysis.cpp
   MCLabel.cpp
+  MCLinkerOptimizationHint.cpp
   MCMachOStreamer.cpp
   MCMachObjectTargetWriter.cpp
   MCModule.cpp
index 59af4101afa86452134dc35e46cb23a6b28da1c1..cc30d056a215c3a2c3e004891eef427095aea20b 100644 (file)
@@ -126,6 +126,7 @@ public:
   void ChangeSection(const MCSection *Section,
                      const MCExpr *Subsection) override;
 
+  void EmitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) override;
   void EmitLabel(MCSymbol *Symbol) override;
   void EmitDebugLabel(MCSymbol *Symbol) override;
 
@@ -342,6 +343,27 @@ void MCAsmStreamer::EmitLabel(MCSymbol *Symbol) {
   EmitEOL();
 }
 
+void MCAsmStreamer::EmitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) {
+  StringRef str = MCLOHIdToName(Kind);
+
+#ifndef NDEBUG
+  int NbArgs = MCLOHIdToNbArgs(Kind);
+  assert(NbArgs != -1 && ((size_t)NbArgs) == Args.size() && "Malformed LOH!");
+  assert(str != "" && "Invalid LOH name");
+#endif
+
+  OS << "\t" << MCLOHDirectiveName() << " " << str << "\t";
+  bool IsFirst = true;
+  for (MCLOHArgs::const_iterator It = Args.begin(), EndIt = Args.end();
+       It != EndIt; ++It) {
+    if (!IsFirst)
+      OS << ", ";
+    IsFirst = false;
+    OS << **It;
+  }
+  EmitEOL();
+}
+
 void MCAsmStreamer::EmitDebugLabel(MCSymbol *Symbol) {
   assert(Symbol->isUndefined() && "Cannot define a symbol twice!");
   MCStreamer::EmitDebugLabel(Symbol);
index 5536087bd0cc43b315e09285a627812468a21696..724ca292bef48c1b9afd9ed80ee1222ecd363688 100644 (file)
@@ -355,6 +355,7 @@ void MCAssembler::reset() {
   getBackend().reset();
   getEmitter().reset();
   getWriter().reset();
+  getLOHContainer().reset();
 }
 
 bool MCAssembler::isSymbolLinkerVisible(const MCSymbol &Symbol) const {
diff --git a/lib/MC/MCLinkerOptimizationHint.cpp b/lib/MC/MCLinkerOptimizationHint.cpp
new file mode 100644 (file)
index 0000000..628a615
--- /dev/null
@@ -0,0 +1,30 @@
+//===-- llvm/MC/MCLinkerOptimizationHint.cpp ----- LOH handling -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCLinkerOptimizationHint.h"
+#include "llvm/MC/MCAsmLayout.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/MC/MCStreamer.h"
+
+using namespace llvm;
+
+namespace llvm {
+template<>
+void MCLOHDirective::Emit_impl(raw_ostream &OutStream,
+                               const MachObjectWriter &ObjWriter,
+                               const MCAsmLayout &Layout) const {
+  const MCAssembler &Asm = Layout.getAssembler();
+  encodeULEB128(Kind, OutStream);
+  encodeULEB128(Args.size(), OutStream);
+  for (LOHArgs::const_iterator It = Args.begin(), EndIt = Args.end();
+       It != EndIt; ++It)
+    encodeULEB128(ObjWriter.getSymbolAddress(&Asm.getSymbolData(**It), Layout),
+                  OutStream);
+}
+} // end namespace llvm
index 742f7846496b9e428587fa8397f0a19b5bbc795e..7e437f4bcb8e331deea7dbef3e9aa244a193aa8d 100644 (file)
@@ -17,6 +17,7 @@
 #include "llvm/MC/MCDwarf.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCLinkerOptimizationHint.h"
 #include "llvm/MC/MCMachOSymbolFlags.h"
 #include "llvm/MC/MCObjectFileInfo.h"
 #include "llvm/MC/MCObjectStreamer.h"
@@ -104,6 +105,10 @@ public:
     llvm_unreachable("macho doesn't support this directive");
   }
 
+  void EmitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) override {
+    getAssembler().getLOHContainer().addDirective(Kind, Args);
+  }
+
   void FinishImpl() override;
 };
 
index bbe589f3385c2d1763173c9c694389e38733b39f..5fcea5f06c1be13f6633949b1c89b23de8c4abef 100644 (file)
@@ -761,6 +761,14 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm,
     LoadCommandsSize += sizeof(MachO::linkedit_data_command);
   }
 
+  // Add the loh load command size, if used.
+  uint64_t LOHRawSize = Asm.getLOHContainer().getEmitSize(*this, Layout);
+  uint64_t LOHSize = RoundUpToAlignment(LOHRawSize, is64Bit() ? 8 : 4);
+  if (LOHSize) {
+    ++NumLoadCommands;
+    LoadCommandsSize += sizeof(MachO::linkedit_data_command);
+  }
+
   // Add the symbol table load command sizes, if used.
   unsigned NumSymbols = LocalSymbolData.size() + ExternalSymbolData.size() +
     UndefinedSymbolData.size();
@@ -849,6 +857,12 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm,
                              DataRegionsSize);
   }
 
+  // Write the loh load command, if used.
+  uint64_t LOHTableEnd = DataInCodeTableEnd + LOHSize;
+  if (LOHSize)
+    WriteLinkeditLoadCommand(MachO::LC_LINKER_OPTIMIZATION_HINT,
+                             DataInCodeTableEnd, LOHSize);
+
   // Write the symbol table load command, if used.
   if (NumSymbols) {
     unsigned FirstLocalSymbol = 0;
@@ -865,10 +879,10 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm,
 
     // If used, the indirect symbols are written after the section data.
     if (NumIndirectSymbols)
-      IndirectSymbolOffset = DataInCodeTableEnd;
+      IndirectSymbolOffset = LOHTableEnd;
 
     // The symbol table is written after the indirect symbol data.
-    uint64_t SymbolTableOffset = DataInCodeTableEnd + IndirectSymbolSize;
+    uint64_t SymbolTableOffset = LOHTableEnd + IndirectSymbolSize;
 
     // The string table is written after symbol table.
     uint64_t StringTableOffset =
@@ -935,6 +949,17 @@ void MachObjectWriter::WriteObject(MCAssembler &Asm,
     Write16(Data->Kind);
   }
 
+  // Write out the loh commands, if there is one.
+  if (LOHSize) {
+#ifndef NDEBUG
+    unsigned Start = OS.tell();
+#endif
+    Asm.getLOHContainer().Emit(*this, Layout);
+    // Pad to a multiple of the pointer size.
+    WriteBytes("", OffsetToAlignment(LOHRawSize, is64Bit() ? 8 : 4));
+    assert(OS.tell() - Start == LOHSize);
+  }
+
   // Write the symbol table data, if used.
   if (NumSymbols) {
     // Write the indirect symbol entries.