Index: llvm/trunk/include/llvm/MC/MCAsmBackend.h =================================================================== --- llvm/trunk/include/llvm/MC/MCAsmBackend.h +++ llvm/trunk/include/llvm/MC/MCAsmBackend.h @@ -87,6 +87,22 @@ 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: llvm/trunk/lib/MC/MCAssembler.cpp =================================================================== --- llvm/trunk/lib/MC/MCAssembler.cpp +++ llvm/trunk/lib/MC/MCAssembler.cpp @@ -322,6 +322,13 @@ const MCAlignFragment &AF = cast(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 @@ if (isa(&Frag) && isa(&Frag)) continue; - if (!isa(&Frag) && !isa(&Frag)) + if (!isa(&Frag) && !isa(&Frag) && + !isa(&Frag)) continue; ArrayRef Fixups; MutableArrayRef Contents; @@ -825,6 +833,13 @@ } else if (auto *FragWithFixups = dyn_cast(&Frag)) { Fixups = FragWithFixups->getFixups(); Contents = FragWithFixups->getContents(); + } else if (auto *AF = dyn_cast(&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: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -40,6 +40,16 @@ 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 Data, uint64_t Value, bool IsResolved, @@ -84,7 +94,8 @@ { "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: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -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 @@ } } +// 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 RISCVAsmBackend::createObjectTargetWriter() const { return createRISCVELFObjectWriter(OSABI, Is64Bit); Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -95,6 +95,8 @@ 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: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -52,6 +52,10 @@ // 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, Index: llvm/trunk/test/MC/RISCV/align.s =================================================================== --- llvm/trunk/test/MC/RISCV/align.s +++ llvm/trunk/test/MC/RISCV/align.s @@ -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