[RISCV] Insert R_RISCV_ALIGN relocation type and Nops for code alignment when linker...
authorShiva Chen <shiva0217@gmail.com>
Wed, 30 Jan 2019 11:16:59 +0000 (11:16 +0000)
committerShiva Chen <shiva0217@gmail.com>
Wed, 30 Jan 2019 11:16:59 +0000 (11:16 +0000)
Linker relaxation may change code size. We need to fix up the alignment
of alignment directive in text section by inserting Nops and R_RISCV_ALIGN
relocation type. So then linker could satisfy the alignment by removing Nops.

To do this:

1. Add shouldInsertExtraNopBytesForCodeAlign target hook to calculate
   the Nops we need to insert.

2. Add shouldInsertFixupForCodeAlign target hook to insert
   R_RISCV_ALIGN fixup type.

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

llvm-svn: 352616

llvm/include/llvm/MC/MCAsmBackend.h
llvm/lib/MC/MCAssembler.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
llvm/test/MC/RISCV/align.s [new file with mode: 0644]

index 3cc0541..1f3ad6c 100644 (file)
@@ -87,6 +87,22 @@ public:
     return false;
   }
 
+  /// Hook to check if extra nop bytes must be inserted for alignment directive.
+  /// For some targets this may be necessary in order to support linker
+  /// relaxation. The number of bytes to insert are returned in Size.
+  virtual bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF,
+                                                     unsigned &Size) {
+    return false;
+  }
+
+  /// Hook which indicates if the target requires a fixup to be generated when
+  /// handling an align directive in an executable section
+  virtual bool shouldInsertFixupForCodeAlign(MCAssembler &Asm,
+                                             const MCAsmLayout &Layout,
+                                             MCAlignFragment &AF) {
+    return false;
+  }
+
   /// Apply the \p Value for given \p Fixup into the provided data fragment, at
   /// the offset specified by the fixup and following the fixup kind as
   /// appropriate. Errors (such as an out of range fixup value) should be
index 8a46699..c4f4d4c 100644 (file)
@@ -322,6 +322,13 @@ uint64_t MCAssembler::computeFragmentSize(const MCAsmLayout &Layout,
     const MCAlignFragment &AF = cast<MCAlignFragment>(F);
     unsigned Offset = Layout.getFragmentOffset(&AF);
     unsigned Size = OffsetToAlignment(Offset, AF.getAlignment());
+
+    // Insert extra Nops for code alignment if the target define
+    // shouldInsertExtraNopBytesForCodeAlign target hook.
+    if (AF.getParent()->UseCodeAlign() && AF.hasEmitNops() &&
+        getBackend().shouldInsertExtraNopBytesForCodeAlign(AF, Size))
+      return Size;
+
     // If we are padding with nops, force the padding to be larger than the
     // minimum nop size.
     if (Size > 0 && AF.hasEmitNops()) {
@@ -804,7 +811,8 @@ void MCAssembler::layout(MCAsmLayout &Layout) {
       if (isa<MCEncodedFragment>(&Frag) &&
           isa<MCCompactEncodedInstFragment>(&Frag))
         continue;
-      if (!isa<MCEncodedFragment>(&Frag) && !isa<MCCVDefRangeFragment>(&Frag))
+      if (!isa<MCEncodedFragment>(&Frag) && !isa<MCCVDefRangeFragment>(&Frag) &&
+          !isa<MCAlignFragment>(&Frag))
         continue;
       ArrayRef<MCFixup> Fixups;
       MutableArrayRef<char> Contents;
@@ -825,6 +833,13 @@ void MCAssembler::layout(MCAsmLayout &Layout) {
       } else if (auto *FragWithFixups = dyn_cast<MCDwarfLineAddrFragment>(&Frag)) {
         Fixups = FragWithFixups->getFixups();
         Contents = FragWithFixups->getContents();
+      } else if (auto *AF = dyn_cast<MCAlignFragment>(&Frag)) {
+        // Insert fixup type for code alignment if the target define
+        // shouldInsertFixupForCodeAlign target hook.
+        if (Sec.UseCodeAlign() && AF->hasEmitNops()) {
+          getBackend().shouldInsertFixupForCodeAlign(*this, Layout, *AF);
+        }
+        continue;
       } else
         llvm_unreachable("Unknown fragment with fixups!");
       for (const MCFixup &Fixup : Fixups) {
index 30cb5cc..7ce058d 100644 (file)
@@ -16,6 +16,7 @@
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCObjectWriter.h"
 #include "llvm/MC/MCSymbol.h"
+#include "llvm/MC/MCValue.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -286,6 +287,57 @@ void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
   }
 }
 
+// Linker relaxation may change code size. We have to insert Nops
+// for .align directive when linker relaxation enabled. So then Linker
+// could satisfy alignment by removing Nops.
+// The function return the total Nops Size we need to insert.
+bool RISCVAsmBackend::shouldInsertExtraNopBytesForCodeAlign(
+    const MCAlignFragment &AF, unsigned &Size) {
+  // Calculate Nops Size only when linker relaxation enabled.
+  if (!STI.getFeatureBits()[RISCV::FeatureRelax])
+    return false;
+
+  bool HasStdExtC = STI.getFeatureBits()[RISCV::FeatureStdExtC];
+  unsigned MinNopLen = HasStdExtC ? 2 : 4;
+
+  Size = AF.getAlignment() - MinNopLen;
+  return true;
+}
+
+// We need to insert R_RISCV_ALIGN relocation type to indicate the
+// position of Nops and the total bytes of the Nops have been inserted
+// when linker relaxation enabled.
+// The function insert fixup_riscv_align fixup which eventually will
+// transfer to R_RISCV_ALIGN relocation type.
+bool RISCVAsmBackend::shouldInsertFixupForCodeAlign(MCAssembler &Asm,
+                                                    const MCAsmLayout &Layout,
+                                                    MCAlignFragment &AF) {
+  // Insert the fixup only when linker relaxation enabled.
+  if (!STI.getFeatureBits()[RISCV::FeatureRelax])
+    return false;
+
+  // Calculate total Nops we need to insert.
+  unsigned Count;
+  shouldInsertExtraNopBytesForCodeAlign(AF, Count);
+  // No Nop need to insert, simply return.
+  if (Count == 0)
+    return false;
+
+  MCContext &Ctx = Asm.getContext();
+  const MCExpr *Dummy = MCConstantExpr::create(0, Ctx);
+  // Create fixup_riscv_align fixup.
+  MCFixup Fixup =
+      MCFixup::create(0, Dummy, MCFixupKind(RISCV::fixup_riscv_align), SMLoc());
+
+  uint64_t FixedValue = 0;
+  MCValue NopBytes = MCValue::get(Count);
+
+  Asm.getWriter().recordRelocation(Asm, Layout, &AF, Fixup, NopBytes,
+                                   FixedValue);
+
+  return true;
+}
+
 std::unique_ptr<MCObjectTargetWriter>
 RISCVAsmBackend::createObjectTargetWriter() const {
   return createRISCVELFObjectWriter(OSABI, Is64Bit);
index c7e5998..6fbc02f 100644 (file)
@@ -40,6 +40,16 @@ public:
   bool requiresDiffExpressionRelocations() const override {
     return STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs;
   }
+
+  // Return Size with extra Nop Bytes for alignment directive in code section.
+  bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF,
+                                             unsigned &Size) override;
+
+  // Insert target specific fixup type for alignment directive in code section.
+  bool shouldInsertFixupForCodeAlign(MCAssembler &Asm,
+                                     const MCAsmLayout &Layout,
+                                     MCAlignFragment &AF) override;
+
   void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
                   const MCValue &Target, MutableArrayRef<char> Data,
                   uint64_t Value, bool IsResolved,
@@ -84,7 +94,8 @@ public:
       { "fixup_riscv_rvc_jump",      2,     11,  MCFixupKindInfo::FKF_IsPCRel },
       { "fixup_riscv_rvc_branch",    0,     16,  MCFixupKindInfo::FKF_IsPCRel },
       { "fixup_riscv_call",          0,     64,  MCFixupKindInfo::FKF_IsPCRel },
-      { "fixup_riscv_relax",         0,      0,  0 }
+      { "fixup_riscv_relax",         0,      0,  0 },
+      { "fixup_riscv_align",         0,      0,  0 }
     };
     static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds,
                   "Not all fixup kinds added to Infos array");
index 506047a..4cbbbcb 100644 (file)
@@ -95,6 +95,8 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
     return ELF::R_RISCV_CALL;
   case RISCV::fixup_riscv_relax:
     return ELF::R_RISCV_RELAX;
+  case RISCV::fixup_riscv_align:
+    return ELF::R_RISCV_ALIGN;
   }
 }
 
index 1a49585..3748a1f 100644 (file)
@@ -52,6 +52,10 @@ enum Fixups {
   // fixup_riscv_relax - Used to generate an R_RISCV_RELAX relocation type,
   // which indicates the linker may relax the instruction pair.
   fixup_riscv_relax,
+  // fixup_riscv_align - Used to generate an R_RISCV_ALIGN relocation type,
+  // which indicates the linker should fixup the alignment after linker
+  // relaxation.
+  fixup_riscv_align,
 
   // fixup_riscv_invalid - used as a sentinel and a marker, must be last fixup
   fixup_riscv_invalid,
diff --git a/llvm/test/MC/RISCV/align.s b/llvm/test/MC/RISCV/align.s
new file mode 100644 (file)
index 0000000..e62af93
--- /dev/null
@@ -0,0 +1,105 @@
+# The file testing Nop insertion with R_RISCV_ALIGN for relaxation.
+
+# Relaxation enabled:
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+relax < %s \
+# RUN:     | llvm-objdump -d -riscv-no-aliases - \
+# RUN:     | FileCheck -check-prefix=RELAX-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+relax < %s \
+# RUN:     | llvm-readobj -r | FileCheck -check-prefix=RELAX-RELOC %s
+
+# Relaxation disabled:
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=-relax < %s \
+# RUN:     | llvm-objdump -d -riscv-no-aliases - \
+# RUN:     | FileCheck -check-prefix=NORELAX-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=-relax < %s \
+# RUN:     | llvm-readobj -r | FileCheck -check-prefix=NORELAX-RELOC %s
+
+# Relaxation enabled with C extension:
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,+relax < %s \
+# RUN:     | llvm-objdump -d -riscv-no-aliases - \
+# RUN:     | FileCheck -check-prefix=C-EXT-RELAX-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,+relax < %s \
+# RUN:     | llvm-readobj -r | FileCheck -check-prefix=C-EXT-RELAX-RELOC %s
+
+# Relaxation disabled with C extension:
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,-relax < %s \
+# RUN:     | llvm-objdump -d -riscv-no-aliases - \
+# RUN:     | FileCheck -check-prefix=C-EXT-NORELAX-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,-relax < %s \
+# RUN:     | llvm-readobj -r | FileCheck -check-prefix=C-EXT-NORELAX-RELOC %s
+
+# We need to insert N-MinNopSize bytes NOPs and R_RISCV_ALIGN relocation
+# type for .align N directive when linker relaxation enabled.
+# Linker could satisfy alignment by removing NOPs after linker relaxation.
+
+# The first R_RISCV_ALIGN come from
+# MCELFStreamer::InitSections() EmitCodeAlignment(4).
+# C-EXT-RELAX-RELOC: R_RISCV_ALIGN - 0x2
+# C-EXT-RELAX-INST:  c.nop
+test:
+       .p2align 2
+# C-EXT-RELAX-RELOC: R_RISCV_ALIGN - 0x2
+# C-EXT-RELAX-INST:  c.nop
+       bne     zero, a0, .LBB0_2
+       mv      a0, zero
+       .p2align 3
+# RELAX-RELOC: R_RISCV_ALIGN - 0x4
+# RELAX-INST:  addi    zero, zero, 0
+# C-EXT-RELAX-RELOC: R_RISCV_ALIGN - 0x6
+# C-EXT-RELAX-INST:  addi    zero, zero, 0
+# C-EXT-RELAX-INST:  c.nop
+# C-EXT-NORELAX-INST: addi    zero, zero, 0
+       add     a0, a0, a1
+       .align 4
+.LBB0_2:
+# RELAX-RELOC: R_RISCV_ALIGN - 0xC
+# RELAX-INST:  addi    zero, zero, 0
+# RELAX-INST:  addi    zero, zero, 0
+# RELAX-INST:  addi    zero, zero, 0
+# NORELAX-INST: addi    zero, zero, 0
+# C-EXT-RELAX-RELOC: R_RISCV_ALIGN - 0xE
+# C-EXT-RELAX-INST:  addi    zero, zero, 0
+# C-EXT-RELAX-INST:  addi    zero, zero, 0
+# C-EXT-RELAX-INST:  addi    zero, zero, 0
+# C-EXT-RELAX-INST:  c.nop
+# C-EXT-INST: addi    zero, zero, 0
+# C-EXT-INST: c.nop
+       add     a0, a0, a1
+       .p2align 3
+.constant_pool:
+.long  3126770193
+# RELAX-RELOC: R_RISCV_ALIGN - 0x4
+# RELAX-INST:  addi    zero, zero, 0
+# NORELAX-INST: addi    zero, zero, 0
+# C-EXT-RELAX-RELOC: R_RISCV_ALIGN - 0x6
+# C-EXT-RELAX-INST:  addi    zero, zero, 0
+# C-EXT-RELAX-INST:  c.nop
+# C-EXT-INST: addi    zero, zero, 0
+# C-EXT-INST: c.nop
+       add     a0, a0, a1
+# Alignment directive with specific padding value 0x01.
+# We will not emit R_RISCV_ALIGN in this case.
+# The behavior is the same as GNU assembler.
+       .p2align 4, 1
+# RELAX-RELOC-NOT: R_RISCV_ALIGN - 0xC
+# RELAX-INST:  01 01
+# RELAX-INST:  01 01
+# C-EXT-RELAX-RELOC-NOT: R_RISCV_ALIGN - 0xE
+# C-EXT-RELAX-INST:  01 01
+# C-EXT-INST:  01 01
+       ret
+# NORELAX-RELOC-NOT: R_RISCV
+# C-EXT-NORELAX-RELOC-NOT: R_RISCV
+# We only need to insert R_RISCV_ALIGN for code section
+# when the linker relaxation enabled.
+        .data
+       .p2align        3
+# RELAX-RELOC-NOT: R_RISCV_ALIGN
+# C-EXT-RELAX-RELOC-NOT: R_RISCV_ALIGN
+data1:
+       .word 7
+       .p2align        4
+# RELAX-RELOC-NOT: R_RISCV_ALIGN
+# C-EXT-RELAX-RELOC-NOT: R_RISCV_ALIGN
+data2:
+       .word 9