Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -7,12 +7,14 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVAsmBackend.h" #include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "MCTargetDesc/RISCVTargetStreamer.h" #include "Utils/RISCVBaseInfo.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" @@ -1164,6 +1166,21 @@ bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { + // Ensure that if the instruction occurs when relaxation is enabled, + // relocations are forced for the file. Ideally this would be done when there + // is enough information to reliably determine if the instruction itself may + // cause relaxations. Unfortunately instruction processing stage occurs in the + // same pass as relocation emission, so it's too late to set a 'sticky bit' + // for the entire file. + if (getSTI().getFeatureBits()[RISCV::FeatureRelax]) { + auto *Assembler = getTargetStreamer().getStreamer().getAssemblerPtr(); + if (Assembler != nullptr) { + RISCVAsmBackend &MAB = + static_cast(Assembler->getBackend()); + MAB.setForceRelocs(); + } + } + // First operand is token for instruction Operands.push_back(RISCVOperand::createToken(Name, NameLoc, isRV64())); @@ -1291,9 +1308,33 @@ return false; } + if (Option == "relax") { + getTargetStreamer().emitDirectiveOptionRelax(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + setFeatureBits(RISCV::FeatureRelax, "relax"); + return false; + } + + if (Option == "norelax") { + getTargetStreamer().emitDirectiveOptionNoRelax(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + clearFeatureBits(RISCV::FeatureRelax, "relax"); + return false; + } + // Unknown option. Warning(Parser.getTok().getLoc(), - "unknown option, expected 'rvc' or 'norvc'"); + "unknown option, expected 'rvc', 'norvc', 'relax' or 'norelax'"); Parser.eatToEndOfStatement(); return false; } Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h =================================================================== --- /dev/null +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -0,0 +1,118 @@ +//===-- RISCVAsmBackend.h - RISCV Assembler Backend -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVASMBACKEND_H +#define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVASMBACKEND_H + +#include "MCTargetDesc/RISCVFixupKinds.h" +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" + +namespace llvm { +class MCAssembler; +class MCObjectTargetWriter; +class raw_ostream; + +class RISCVAsmBackend : public MCAsmBackend { + const MCSubtargetInfo &STI; + uint8_t OSABI; + bool Is64Bit; + bool ForceRelocs = false; + +public: + RISCVAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit) + : MCAsmBackend(support::little), STI(STI), OSABI(OSABI), + Is64Bit(Is64Bit) {} + ~RISCVAsmBackend() override {} + + void setForceRelocs() { ForceRelocs = true; } + + // Generate diff expression relocations if the relax feature is enabled or had + // previously been enabled, otherwise it is safe for the assembler to + // calculate these internally. + bool requiresDiffExpressionRelocations() const override { + return STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs; + } + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override; + + std::unique_ptr + createObjectTargetWriter() const override; + + // If linker relaxation is enabled, or the relax option had previously been + // enabled, always emit relocations even if the fixup can be resolved. This is + // necessary for correctness as offsets may change during relaxation. + bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target) override { + return STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs; + } + + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override { + llvm_unreachable("Handled by fixupNeedsRelaxationAdvanced"); + } + + bool fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved, + uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout, + const bool WasForced) const override; + + unsigned getNumFixupKinds() const override { + return RISCV::NumTargetFixupKinds; + } + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { + const static MCFixupKindInfo Infos[] = { + // This table *must* be in the order that the fixup_* kinds are defined in + // RISCVFixupKinds.h. + // + // name offset bits flags + { "fixup_riscv_hi20", 12, 20, 0 }, + { "fixup_riscv_lo12_i", 20, 12, 0 }, + { "fixup_riscv_lo12_s", 0, 32, 0 }, + { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, + { "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 } + }; + static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds, + "Not all fixup kinds added to Infos array"); + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; + } + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override; + unsigned getRelaxedOpcode(unsigned Op) const; + + void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + MCInst &Res) const override; + + + bool writeNopData(raw_ostream &OS, uint64_t Count) const override; +}; +} + +#endif Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -7,115 +7,20 @@ // //===----------------------------------------------------------------------===// -#include "MCTargetDesc/RISCVFixupKinds.h" -#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "RISCVAsmBackend.h" #include "llvm/ADT/APInt.h" -#include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" -#include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; -namespace { -class RISCVAsmBackend : public MCAsmBackend { - const MCSubtargetInfo &STI; - uint8_t OSABI; - bool Is64Bit; - -public: - RISCVAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit) - : MCAsmBackend(support::little), STI(STI), OSABI(OSABI), - Is64Bit(Is64Bit) {} - ~RISCVAsmBackend() override {} - - // Generate diff expression relocations if the relax feature is enabled, - // otherwise it is safe for the assembler to calculate these internally. - bool requiresDiffExpressionRelocations() const override { - return STI.getFeatureBits()[RISCV::FeatureRelax]; - } - void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, - const MCValue &Target, MutableArrayRef Data, - uint64_t Value, bool IsResolved, - const MCSubtargetInfo *STI) const override; - - std::unique_ptr - createObjectTargetWriter() const override; - - // If linker relaxation is enabled, always emit relocations even if the fixup - // can be resolved. This is necessary for correctness as offsets may change - // during relaxation. - bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, - const MCValue &Target) override { - return STI.getFeatureBits()[RISCV::FeatureRelax]; - } - - bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, - const MCRelaxableFragment *DF, - const MCAsmLayout &Layout) const override { - llvm_unreachable("Handled by fixupNeedsRelaxationAdvanced"); - } - - bool fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved, - uint64_t Value, - const MCRelaxableFragment *DF, - const MCAsmLayout &Layout, - const bool WasForced) const override; - - unsigned getNumFixupKinds() const override { - return RISCV::NumTargetFixupKinds; - } - - const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { - const static MCFixupKindInfo Infos[] = { - // This table *must* be in the order that the fixup_* kinds are defined in - // RISCVFixupKinds.h. - // - // name offset bits flags - { "fixup_riscv_hi20", 12, 20, 0 }, - { "fixup_riscv_lo12_i", 20, 12, 0 }, - { "fixup_riscv_lo12_s", 0, 32, 0 }, - { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, - { "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 } - }; - static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds, - "Not all fixup kinds added to Infos array"); - - if (Kind < FirstTargetFixupKind) - return MCAsmBackend::getFixupKindInfo(Kind); - - assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && - "Invalid kind!"); - return Infos[Kind - FirstTargetFixupKind]; - } - - bool mayNeedRelaxation(const MCInst &Inst, - const MCSubtargetInfo &STI) const override; - unsigned getRelaxedOpcode(unsigned Op) const; - - void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, - MCInst &Res) const override; - - - bool writeNopData(raw_ostream &OS, uint64_t Count) const override; -}; - - bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved, uint64_t Value, @@ -348,8 +253,6 @@ return createRISCVELFObjectWriter(OSABI, Is64Bit); } -} // end anonymous namespace - MCAsmBackend *llvm::createRISCVAsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, Index: lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h +++ lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h @@ -22,6 +22,8 @@ virtual void emitDirectiveOptionRVC(); virtual void emitDirectiveOptionNoRVC(); + virtual void emitDirectiveOptionRelax(); + virtual void emitDirectiveOptionNoRelax(); }; } #endif Index: lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp @@ -40,3 +40,5 @@ void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {} void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {} +void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {} +void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {} Index: lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h +++ lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h @@ -20,6 +20,8 @@ virtual void emitDirectiveOptionRVC() = 0; virtual void emitDirectiveOptionNoRVC() = 0; + virtual void emitDirectiveOptionRelax() = 0; + virtual void emitDirectiveOptionNoRelax() = 0; }; // This part is for ascii assembly output @@ -31,6 +33,8 @@ void emitDirectiveOptionRVC() override; void emitDirectiveOptionNoRVC() override; + void emitDirectiveOptionRelax() override; + void emitDirectiveOptionNoRelax() override; }; } Index: lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -30,3 +30,11 @@ void RISCVTargetAsmStreamer::emitDirectiveOptionNoRVC() { OS << "\t.option\tnorvc\n"; } + +void RISCVTargetAsmStreamer::emitDirectiveOptionRelax() { + OS << "\t.option\trelax\n"; +} + +void RISCVTargetAsmStreamer::emitDirectiveOptionNoRelax() { + OS << "\t.option\tnorelax\n"; +} Index: test/CodeGen/RISCV/fixups-relax-diff.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/fixups-relax-diff.ll @@ -0,0 +1,20 @@ +; RUN: llc -filetype=obj -mtriple=riscv32 -mattr=+relax %s -o - \ +; RUN: | llvm-readobj -r | FileCheck -check-prefix=RELAX %s +; RUN: llc -filetype=obj -mtriple=riscv32 -mattr=-relax %s -o - \ +; RUN: | llvm-readobj -r | FileCheck -check-prefix=NORELAX %s + +; This test checks that a diff inserted via inline assembly only causes +; relocations when relaxation is enabled. This isn't an assembly test +; as the assembler takes a different path through LLVM, which is +; already covered by the fixups-expr.s test. + +define i32 @main() { +entry: + %retval = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + ; RELAX: R_RISCV_ADD64 b + ; RELAX: R_RISCV_SUB64 a + ; NORELAX-NOT: R_RISCV + call void asm sideeffect "a:\0Ab:\0A.dword b-a", ""() + ret i32 0 +} Index: test/CodeGen/RISCV/option-norelax.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/option-norelax.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple=riscv32 -mattr=+relax -filetype=obj < %s \ +; RUN: | llvm-objdump -d -r - | FileCheck %s + +; This test demonstrates that .option norelax has no effect on codegen +; when emitting an ELF directly. + +declare i32 @foo(i32) + +define i32 @bar(i32 %a) nounwind { +; CHECK-LABEL: bar: +; CHECK: R_RISCV_CALL +; CHECK: R_RISCV_RELAX + tail call void asm sideeffect ".option norelax", ""() + %1 = call i32 @foo(i32 %a) + ret i32 %1 +} Index: test/CodeGen/RISCV/option-relax.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/option-relax.ll @@ -0,0 +1,16 @@ +; RUN: llc -mtriple=riscv32 -mattr=-relax -filetype=obj < %s \ +; RUN: | llvm-objdump -d -r - | FileCheck %s + +; This test demonstrates that .option relax has no effect on codegen +; when emitting an ELF directly. + +declare i32 @foo(i32) + +define i32 @bar(i32 %a) nounwind { +; CHECK-LABEL: bar: +; CHECK: R_RISCV_CALL +; CHECK-NOT: R_RISCV_RELAX + tail call void asm sideeffect ".option relax", ""() + %1 = call i32 @foo(i32 %a) + ret i32 %1 +} Index: test/MC/RISCV/option-invalid.s =================================================================== --- test/MC/RISCV/option-invalid.s +++ test/MC/RISCV/option-invalid.s @@ -13,5 +13,5 @@ # CHECK: error: unexpected token, expected end of statement .option rvc foo -# CHECK: warning: unknown option, expected 'rvc' or 'norvc' +# CHECK: warning: unknown option, expected 'rvc', 'norvc', 'relax' or 'norelax' .option bar Index: test/MC/RISCV/option-relax.s =================================================================== --- /dev/null +++ test/MC/RISCV/option-relax.s @@ -0,0 +1,66 @@ +# RUN: llvm-mc -triple riscv32 < %s \ +# RUN: | FileCheck -check-prefix=CHECK-INST %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=CHECK-RELOC %s + +# RUN: llvm-mc -triple riscv64 < %s \ +# RUN: | FileCheck -check-prefix=CHECK-INST %s +# RUN: llvm-mc -filetype=obj -triple riscv64 < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=CHECK-RELOC %s + +# Check .option relax causes R_RISCV_RELAX to be emitted, and .option +# norelax suppresses it. Also check that if .option relax was enabled +# at any point and an instruction may have been relaxed, diff & branch +# relocations are emitted to ensure correct codegen. See +# linker-relaxation.s and fixups-expr.s for behaviour of the relax +# attribute. + +.L1: +.option norelax +# CHECK-INST: .option norelax + +# CHECK-INST: call foo +# CHECK-RELOC: R_RISCV_CALL foo 0x0 +# CHECK-RELOC-NOT: R_RISCV_RELAX foo 0x0 +call foo + +# CHECK-RELOC-NEXT: R_RISCV_ADD64 +# CHECK-RELOC-NEXT: R_RISCV_SUB64 +.dword .L2-.L1 +# CHECK-RELOC-NEXT: R_RISCV_JAL +jal zero, .L1 +# CHECK-RELOC-NEXT: R_RISCV_BRANCH +beq s1, s1, .L1 + +.L2: +.option relax +# CHECK-INST: .option relax + +# CHECK-INST: call bar +# CHECK-RELOC-NEXT: R_RISCV_CALL bar 0x0 +# CHECK-RELOC-NEXT: R_RISCV_RELAX bar 0x0 +call bar + +# CHECK-RELOC-NEXT: R_RISCV_ADD64 +# CHECK-RELOC-NEXT: R_RISCV_SUB64 +.dword .L2-.L1 +# CHECK-RELOC-NEXT: R_RISCV_JAL +jal zero, .L1 +# CHECK-RELOC-NEXT: R_RISCV_BRANCH +beq s1, s1, .L1 + +.option norelax +# CHECK-INST: .option norelax + +# CHECK-INST: call baz +# CHECK-RELOC-NEXT: R_RISCV_CALL baz 0x0 +# CHECK-RELOC-NOT: R_RISCV_RELAX baz 0x0 +call baz + +# CHECK-RELOC-NEXT: R_RISCV_ADD64 +# CHECK-RELOC-NEXT: R_RISCV_SUB64 +.dword .L2-.L1 +# CHECK-RELOC-NEXT: R_RISCV_JAL +jal zero, .L1 +# CHECK-RELOC-NEXT: R_RISCV_BRANCH +beq s1, s1, .L1