diff --git a/llvm/lib/MC/MCFragment.cpp b/llvm/lib/MC/MCFragment.cpp index 6cbdf749a6943..21da79bb0aa30 100644 --- a/llvm/lib/MC/MCFragment.cpp +++ b/llvm/lib/MC/MCFragment.cpp @@ -68,6 +68,8 @@ LLVM_DUMP_METHOD void MCFragment::dump() const { OS << "\n Fixup @" << F.getOffset() << " Value:"; F.getValue()->print(OS, nullptr); OS << " Kind:" << F.getKind(); + if (F.isLinkerRelaxable()) + OS << " LinkerRelaxable"; } }; diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index d0c61449a6cfc..cdd6189d890f3 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -461,11 +461,23 @@ void MCObjectStreamer::emitInstToFragment(const MCInst &Inst, getAssembler().getEmitter().encodeInstruction(Inst, Data, Fixups, STI); F->Kind = MCFragment::FT_Relaxable; - F->STI = &STI; - F->HasInstructions = true; + F->setHasInstructions(STI); + F->setVarContents(Data); - F->setVarFixups(Fixups); F->setInst(Inst); + + bool MarkedLinkerRelaxable = false; + for (auto &Fixup : Fixups) { + if (!Fixup.isLinkerRelaxable() || MarkedLinkerRelaxable) + continue; + MarkedLinkerRelaxable = true; + auto *Sec = F->getParent(); + if (!Sec->isLinkerRelaxable()) + Sec->setFirstLinkerRelaxable(F->getLayoutOrder()); + F->setLinkerRelaxable(); + } + F->setVarFixups(Fixups); + newFragment(); } diff --git a/llvm/lib/MC/MCSection.cpp b/llvm/lib/MC/MCSection.cpp index 9ed6fd1a8b4e4..a668e7919b7b9 100644 --- a/llvm/lib/MC/MCSection.cpp +++ b/llvm/lib/MC/MCSection.cpp @@ -39,6 +39,8 @@ LLVM_DUMP_METHOD void MCSection::dump( raw_ostream &OS = errs(); OS << "MCSection Name:" << getName(); + if (isLinkerRelaxable()) + OS << " FirstLinkerRelaxable:" << firstLinkerRelaxable(); for (auto &F : *this) { OS << '\n'; F.dump(); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 8d956ce41c5bf..96f22c2d1ab73 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -819,6 +819,23 @@ void RISCVAsmBackend::maybeAddVendorReloc(const MCFragment &F, Asm->getWriter().recordRelocation(F, VendorFixup, VendorTarget, VendorValue); } +static bool relaxableFixupNeedsRelocation(const MCFixupKind Kind) { + // Some Fixups are marked as LinkerRelaxable by + // `RISCVMCCodeEmitter::getImmOpValue` only because they may be + // (assembly-)relaxed into a linker-relaxable instruction. This function + // should return `false` for those fixups so they do not get a `R_RISCV_RELAX` + // relocation emitted in addition to the relocation. + switch (Kind) { + default: + break; + case RISCV::fixup_riscv_rvc_jump: + case RISCV::fixup_riscv_rvc_branch: + case RISCV::fixup_riscv_jal: + return false; + } + return true; +} + bool RISCVAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup, const MCValue &Target, uint64_t &FixedValue, bool IsResolved) { @@ -861,25 +878,32 @@ bool RISCVAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup, return false; } - // If linker relaxation is enabled and supported by the current relocation, - // generate a relocation and then append a RELAX. - if (Fixup.isLinkerRelaxable()) + // If linker relaxation is enabled and supported by the current fixup, then we + // always want to generate a relocation. + bool NeedsRelax = Fixup.isLinkerRelaxable() && + relaxableFixupNeedsRelocation(Fixup.getKind()); + if (NeedsRelax) IsResolved = false; + if (IsResolved && Fixup.isPCRel()) IsResolved = isPCRelFixupResolved(Target.getAddSym(), F); if (!IsResolved) { - // Some Fixups require a vendor relocation, record it (directly) before we + // Some Fixups require a VENDOR relocation, record it (directly) before we // add the relocation. maybeAddVendorReloc(F, Fixup); Asm->getWriter().recordRelocation(F, Fixup, Target, FixedValue); - } - if (Fixup.isLinkerRelaxable()) { - auto FA = MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_RISCV_RELAX); - Asm->getWriter().recordRelocation(F, FA, MCValue::get(nullptr), - FixedValueA); + if (NeedsRelax) { + // Some Fixups get a RELAX relocation, record it (directly) after we add + // the relocation. + MCFixup RelaxFixup = + MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_RISCV_RELAX); + MCValue RelaxTarget = MCValue::get(nullptr); + uint64_t RelaxValue; + Asm->getWriter().recordRelocation(F, RelaxFixup, RelaxTarget, RelaxValue); + } } return false; diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index cbeabdddb9371..717fba68b48ed 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -576,8 +576,21 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, "getImmOpValue expects only expressions or immediates"); const MCExpr *Expr = MO.getExpr(); MCExpr::ExprKind Kind = Expr->getKind(); - unsigned FixupKind = RISCV::fixup_riscv_invalid; + + // `RelaxCandidate` must be set to `true` in two cases: + // - The fixup's relocation gets a R_RISCV_RELAX relocation + // - The underlying instruction may be relaxed to an instruction that gets a + // `R_RISCV_RELAX` relocation. + // + // The actual emission of `R_RISCV_RELAX` will be handled in + // `RISCVAsmBackend::applyFixup`. bool RelaxCandidate = false; + auto AsmRelaxToLinkerRelaxableWithFeature = [&](unsigned Feature) -> void { + if (!STI.hasFeature(RISCV::FeatureExactAssembly) && STI.hasFeature(Feature)) + RelaxCandidate = true; + }; + + unsigned FixupKind = RISCV::fixup_riscv_invalid; if (Kind == MCExpr::Specifier) { const auto *RVExpr = cast(Expr); FixupKind = RVExpr->getSpecifier(); @@ -644,18 +657,26 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, // FIXME: Sub kind binary exprs have chance of underflow. if (MIFrm == RISCVII::InstFormatJ) { FixupKind = RISCV::fixup_riscv_jal; + AsmRelaxToLinkerRelaxableWithFeature(RISCV::FeatureVendorXqcilb); } else if (MIFrm == RISCVII::InstFormatB) { FixupKind = RISCV::fixup_riscv_branch; + // This might be assembler relaxed to `b; jal` but we cannot relax + // the `jal` again in the assembler. } else if (MIFrm == RISCVII::InstFormatCJ) { FixupKind = RISCV::fixup_riscv_rvc_jump; + AsmRelaxToLinkerRelaxableWithFeature(RISCV::FeatureVendorXqcilb); } else if (MIFrm == RISCVII::InstFormatCB) { FixupKind = RISCV::fixup_riscv_rvc_branch; + // This might be assembler relaxed to `b; jal` but we cannot relax + // the `jal` again in the assembler. } else if (MIFrm == RISCVII::InstFormatCI) { FixupKind = RISCV::fixup_riscv_rvc_imm; } else if (MIFrm == RISCVII::InstFormatI) { FixupKind = RISCV::fixup_riscv_12_i; } else if (MIFrm == RISCVII::InstFormatQC_EB) { FixupKind = RISCV::fixup_riscv_qc_e_branch; + // This might be assembler relaxed to `qc.e.b; jal` but we cannot + // relax the `jal` again in the assembler. } else if (MIFrm == RISCVII::InstFormatQC_EAI) { FixupKind = RISCV::fixup_riscv_qc_e_32; RelaxCandidate = true; @@ -670,9 +691,9 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, assert(FixupKind != RISCV::fixup_riscv_invalid && "Unhandled expression!"); addFixup(Fixups, 0, Expr, FixupKind); - // If linker relaxation is enabled and supported by this relocation, set - // a bit so that if fixup is unresolved, a R_RISCV_RELAX relocation will be - // appended. + // If linker relaxation is enabled and supported by this relocation, set a bit + // so that the assembler knows the size of the instruction is not fixed/known, + // and the relocation will need a R_RISCV_RELAX relocation. if (EnableRelax && RelaxCandidate) Fixups.back().setLinkerRelaxable(); ++MCNumFixups; diff --git a/llvm/test/MC/RISCV/Relocations/mc-dump.s b/llvm/test/MC/RISCV/Relocations/mc-dump.s index 99d34b5ec9114..bd8acf9dd41e0 100644 --- a/llvm/test/MC/RISCV/Relocations/mc-dump.s +++ b/llvm/test/MC/RISCV/Relocations/mc-dump.s @@ -2,12 +2,12 @@ # RUN: llvm-mc -filetype=obj --triple=riscv64 --mattr=+relax %s -debug-only=mc-dump -o /dev/null 2>&1 | FileCheck %s # CHECK:Sections:[ -# CHECK-NEXT:MCSection Name:.text +# CHECK-NEXT:MCSection Name:.text FirstLinkerRelaxable:1 # CHECK-NEXT:0 Align Size:0+0 [] # CHECK-NEXT: Align:4 Fill:0 FillLen:1 MaxBytesToEmit:4 Nops # CHECK-NEXT: Symbol @0 .text # CHECK-NEXT:0 Data LinkerRelaxable Size:8 [97,00,00,00,e7,80,00,00] -# CHECK-NEXT: Fixup @0 Value:specifier(19,ext) Kind:4023 +# CHECK-NEXT: Fixup @0 Value:specifier(19,ext) Kind:4023 LinkerRelaxable # CHECK-NEXT: Symbol @0 $x # CHECK-NEXT:8 Align LinkerRelaxable Size:0+6 [] # CHECK-NEXT: Align:8 Fill:0 FillLen:1 MaxBytesToEmit:8 Nops diff --git a/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s b/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s new file mode 100644 index 0000000000000..2066b55b41ad3 --- /dev/null +++ b/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s @@ -0,0 +1,84 @@ +# RUN: llvm-mc --triple=riscv32 -mattr=+relax,+experimental-xqcilb,+experimental-xqcibi \ +# RUN: %s -filetype=obj -o - -riscv-add-build-attributes \ +# RUN: | llvm-objdump -dr -M no-aliases - \ +# RUN: | FileCheck %s + +## This tests that we correctly emit relocations for linker relaxation when +## relaxing `JAL` to `QC.E.JAL`. + +## PR150071 + +.global foo + +# CHECK-LABEL: : +branch_over_relaxable: + jal x1, foo +# CHECK: qc.e.jal 0x0 +# CHECK-NEXT: R_RISCV_VENDOR QUALCOMM +# CHECK-NEXT: R_RISCV_CUSTOM195 foo +# CHECK-NEXT: R_RISCV_RELAX *ABS* + bne a0, a1, branch_over_relaxable +# CHECK-NEXT: bne a0, a1, 0x6 +# CHECK-NEXT: R_RISCV_BRANCH branch_over_relaxable +# CHECK-NOT: R_RISCV_RELAX + qc.e.bnei a0, 0x21, branch_over_relaxable +# CHECK-NEXT: qc.e.bnei a0, 0x21, 0xa +# CHECK-NEXT: R_RISCV_VENDOR QUALCOMM +# CHECK-NEXT: R_RISCV_CUSTOM193 branch_over_relaxable +# CHECK-NOT: R_RISCV_RELAX + ret +# CHECK-NEXT: c.jr ra + +# CHECK-LABEL: : +short_jump_over_fixed: + nop +# CHECK: c.nop + j short_jump_over_fixed +# CHECK-NEXT: c.j 0x12 +# CHECK-NOT: R_RISCV_RVC_JUMP +# CHECK-NOT: R_RISCV_RELAX + ret +# CHECK-NEXT: c.jr ra + +# CHECK-LABEL: : +short_jump_over_relaxable: + call foo +# CHECK: auipc ra, 0x0 +# CHECK-NEXT: R_RISCV_CALL_PLT foo +# CHECK-NEXT: R_RISCV_RELAX *ABS* +# CHECK-NEXT: jalr ra, 0x0(ra) + j short_jump_over_relaxable +# CHECK-NEXT: c.j 0x20 +# CHECK-NEXT: R_RISCV_RVC_JUMP short_jump_over_relaxable +# CHECK-NOT: R_RISCV_RELAX + ret +# CHECK-NEXT: c.jr ra + +# CHECK-LABEL: : +mid_jump_over_fixed: + nop +# CHECK: c.nop + .space 0x1000 +# CHECK-NEXT: ... + j mid_jump_over_fixed +# CHECK-NEXT: jal zero, 0x24 +# CHECK-NOT: R_RISCV_JAL +# CHECK-NOT: R_RISCV_RELAX + ret +# CHECK-NEXT: c.jr ra + +# CHECK-LABEL: : +mid_jump_over_relaxable: + call foo +# CHECK: auipc ra, 0x0 +# CHECK-NEXT: R_RISCV_CALL_PLT foo +# CHECK-NEXT: R_RISCV_RELAX *ABS* +# CHECK-NEXT: jalr ra, 0x0(ra) + .space 0x1000 +# CHECK-NEXT: ... + j mid_jump_over_relaxable +# CHECK-NEXT: jal zero, 0x2034 +# CHECK-NEXT: R_RISCV_JAL mid_jump_over_relaxable +# CHECK-NOT: R_RISCV_RELAX + ret +# CHECK-NEXT: c.jr ra