Index: include/llvm/MC/MCAsmBackend.h =================================================================== --- include/llvm/MC/MCAsmBackend.h +++ include/llvm/MC/MCAsmBackend.h @@ -88,6 +88,19 @@ return false; } + /// Hook for the target need to insert extra Nops for alignment directive. + virtual bool + insertNopBytesForAlignDirectiveInTextSection(const MCFragment &F, + unsigned &Size) { + return false; + } + + /// Hook for the target need to insert fixup type for alignment directive. + virtual bool insertFixupForAlignDirectiveInTextSection( + MCAssembler &Asm, const MCAsmLayout &Layout, MCFragment &F) { + 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: lib/MC/MCAssembler.cpp =================================================================== --- lib/MC/MCAssembler.cpp +++ lib/MC/MCAssembler.cpp @@ -322,6 +322,15 @@ const MCAlignFragment &AF = cast(F); unsigned Offset = Layout.getFragmentOffset(&AF); unsigned Size = OffsetToAlignment(Offset, AF.getAlignment()); + + // Insert extra Nops for alignment if the target define + // insertNopBytesForAlignDirectiveInTextSection target hook. + if (auto *ELFSec = dyn_cast(AF.getParent())) { + if ((ELFSec->getSectionName() == StringRef(".text")) && + getBackend().insertNopBytesForAlignDirectiveInTextSection(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()) { @@ -801,7 +810,8 @@ if (isa(&Frag) && isa(&Frag)) continue; - if (!isa(&Frag) && !isa(&Frag)) + if (!isa(&Frag) && !isa(&Frag) && + !isa(&Frag)) continue; ArrayRef Fixups; MutableArrayRef Contents; @@ -814,6 +824,16 @@ } else if (auto *FragWithFixups = dyn_cast(&Frag)) { Fixups = FragWithFixups->getFixups(); Contents = FragWithFixups->getContents(); + } else if (dyn_cast(&Frag)) { + const MCAlignFragment &AF = cast(Frag); + // Insert fixup type for the alignment if the target define + // insertFixupForAlignDirectiveInTextSection target hook. + if (auto *ELFSec = dyn_cast(AF.getParent())) { + if (ELFSec->getSectionName() == StringRef(".text")) + getBackend().insertFixupForAlignDirectiveInTextSection( + *this, Layout, Frag); + } + continue; } else llvm_unreachable("Unknown fragment with fixups!"); for (const MCFixup &Fixup : Fixups) { Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -20,6 +20,7 @@ #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCValue.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -57,6 +58,13 @@ return STI.getFeatureBits()[RISCV::FeatureRelax]; } + bool insertNopBytesForAlignDirectiveInTextSection(const MCFragment &F, + unsigned &Size) override; + + bool insertFixupForAlignDirectiveInTextSection(MCAssembler &Asm, + const MCAsmLayout &Layout, + MCFragment &F) override; + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const override { @@ -90,7 +98,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"); @@ -339,6 +348,53 @@ } } +// 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::insertNopBytesForAlignDirectiveInTextSection( + const MCFragment &F, 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; + + const MCAlignFragment &AF = cast(F); + 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::insertFixupForAlignDirectiveInTextSection( + MCAssembler &Asm, const MCAsmLayout &Layout, MCFragment &F) { + // Insert the fixup only when linker relaxation enabled. + if (!STI.getFeatureBits()[RISCV::FeatureRelax]) + 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; + // Calculate total Nops we need to insert. + unsigned Count; + insertNopBytesForAlignDirectiveInTextSection(F, Count); + MCValue NopBytes = MCValue::get(Count); + + Asm.getWriter().recordRelocation(Asm, Layout, &F, Fixup, NopBytes, + FixedValue); + + return true; +} + std::unique_ptr RISCVAsmBackend::createObjectTargetWriter() const { return createRISCVELFObjectWriter(OSABI, Is64Bit); Index: lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -96,6 +96,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: lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -53,6 +53,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: test/MC/RISCV/align.s =================================================================== --- /dev/null +++ test/MC/RISCV/align.s @@ -0,0 +1,42 @@ +# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,+relax < %s \ +# RUN: | llvm-objdump -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefix=RELAX-INST %s +# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,+relax < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELAX-RELOC %s +# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,-relax < %s \ +# RUN: | llvm-objdump -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefix=NORELAX-INST %s +# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c,-relax < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=NORELAX-RELOC %s + +# We need to insert N-2 bytes NOPs for 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). +# RELAX-RELOC: R_RISCV_ALIGN - 0x2 +# RELAX-INST: c.nop +test: + .p2align 2 +# RELAX-RELOC: R_RISCV_ALIGN - 0x2 +# RELAX-INST: c.nop + c.bnez a0, .LBB0_2 + mv a0, zero + .p2align 3 +# RELAX-RELOC: R_RISCV_ALIGN - 0x6 +# RELAX-INST: addi zero, zero, 0 +# RELAX-INST: c.nop +# NORELAX-INST: addi zero, zero, 0 + add a0, a0, a1 + .align 4 +# RELAX-RELOC: R_RISCV_ALIGN - 0xE +# RELAX-INST: addi zero, zero, 0 +# RELAX-INST: addi zero, zero, 0 +# RELAX-INST: addi zero, zero, 0 +# RELAX-INST: c.nop +# INST: addi zero, zero, 0 +# INST: c.nop +# NORELAX-RELOC-NOT: R_RISCV +.LBB0_2: + ret