diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp index e3abb7eecc32b1..ffc1a27aa6e18f 100644 --- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp +++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp @@ -10,6 +10,7 @@ #include "MCTargetDesc/LoongArchMCExpr.h" #include "MCTargetDesc/LoongArchMCTargetDesc.h" #include "MCTargetDesc/LoongArchMatInt.h" +#include "MCTargetDesc/LoongArchTargetStreamer.h" #include "TargetInfo/LoongArchTargetInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstBuilder.h" @@ -30,8 +31,16 @@ using namespace llvm; namespace { class LoongArchAsmParser : public MCTargetAsmParser { + SmallVector FeatureBitStack; + SMLoc getLoc() const { return getParser().getTok().getLoc(); } bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); } + LoongArchTargetStreamer &getTargetStreamer() { + assert(getParser().getStreamer().getTargetStreamer() && + "do not have a target streamer"); + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast(TS); + } struct Inst { unsigned Opc; @@ -60,6 +69,8 @@ class LoongArchAsmParser : public MCTargetAsmParser { unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; + ParseStatus parseDirective(AsmToken DirectiveID) override; + bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo, int64_t Lower, int64_t Upper, const Twine &Msg); @@ -81,6 +92,39 @@ class LoongArchAsmParser : public MCTargetAsmParser { bool parseOperand(OperandVector &Operands, StringRef Mnemonic); + bool parseDirectiveOption(); + + void setFeatureBits(uint64_t Feature, StringRef FeatureString) { + if (!(getSTI().hasFeature(Feature))) { + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); + } + } + + void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { + if (getSTI().hasFeature(Feature)) { + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); + } + } + + void pushFeatureBits() { + FeatureBitStack.push_back(getSTI().getFeatureBits()); + } + + bool popFeatureBits() { + if (FeatureBitStack.empty()) + return true; + + FeatureBitset FeatureBits = FeatureBitStack.pop_back_val(); + copySTI().setFeatureBits(FeatureBits); + setAvailableFeatures(ComputeAvailableFeatures(FeatureBits)); + + return false; + } + // Helper to emit the sequence of instructions generated by the // "emitLoadAddress*" functions. void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, @@ -1748,6 +1792,70 @@ bool LoongArchAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, llvm_unreachable("Unknown match type detected!"); } +bool LoongArchAsmParser::parseDirectiveOption() { + MCAsmParser &Parser = getParser(); + // Get the option token. + AsmToken Tok = Parser.getTok(); + + // At the moment only identifiers are supported. + if (parseToken(AsmToken::Identifier, "expected identifier")) + return true; + + StringRef Option = Tok.getIdentifier(); + + if (Option == "push") { + if (Parser.parseEOL()) + return true; + + getTargetStreamer().emitDirectiveOptionPush(); + pushFeatureBits(); + return false; + } + + if (Option == "pop") { + SMLoc StartLoc = Parser.getTok().getLoc(); + if (Parser.parseEOL()) + return true; + + getTargetStreamer().emitDirectiveOptionPop(); + if (popFeatureBits()) + return Error(StartLoc, ".option pop with no .option push"); + + return false; + } + + if (Option == "relax") { + if (Parser.parseEOL()) + return true; + + getTargetStreamer().emitDirectiveOptionRelax(); + setFeatureBits(LoongArch::FeatureRelax, "relax"); + return false; + } + + if (Option == "norelax") { + if (Parser.parseEOL()) + return true; + + getTargetStreamer().emitDirectiveOptionNoRelax(); + clearFeatureBits(LoongArch::FeatureRelax, "relax"); + return false; + } + + // Unknown option. + Warning(Parser.getTok().getLoc(), + "unknown option, expected 'push', 'pop', 'relax' or 'norelax'"); + Parser.eatToEndOfStatement(); + return false; +} + +ParseStatus LoongArchAsmParser::parseDirective(AsmToken DirectiveID) { + if (DirectiveID.getString() == ".option") + return parseDirectiveOption(); + + return ParseStatus::NoMatch; +} + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmParser() { RegisterMCAsmParser X(getTheLoongArch32Target()); RegisterMCAsmParser Y(getTheLoongArch64Target()); diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.cpp index eee85822398b14..f2900797c6a2be 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.cpp @@ -35,6 +35,11 @@ MCELFStreamer &LoongArchTargetELFStreamer::getStreamer() { return static_cast(Streamer); } +void LoongArchTargetELFStreamer::emitDirectiveOptionPush() {} +void LoongArchTargetELFStreamer::emitDirectiveOptionPop() {} +void LoongArchTargetELFStreamer::emitDirectiveOptionRelax() {} +void LoongArchTargetELFStreamer::emitDirectiveOptionNoRelax() {} + void LoongArchTargetELFStreamer::finish() { LoongArchTargetStreamer::finish(); ELFObjectWriter &W = getStreamer().getWriter(); diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h index e220729d8923e2..894ea963071487 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h @@ -19,6 +19,11 @@ class LoongArchTargetELFStreamer : public LoongArchTargetStreamer { MCELFStreamer &getStreamer(); LoongArchTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + void emitDirectiveOptionPush() override; + void emitDirectiveOptionPop() override; + void emitDirectiveOptionRelax() override; + void emitDirectiveOptionNoRelax() override; + void finish() override; }; diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp index 595ce9fc815bf0..62e900dc65babb 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp @@ -87,6 +87,12 @@ createLoongArchObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { : nullptr; } +static MCTargetStreamer * +createLoongArchAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, + MCInstPrinter *InstPrint) { + return new LoongArchTargetAsmStreamer(S, OS); +} + namespace { class LoongArchMCInstrAnalysis : public MCInstrAnalysis { @@ -212,5 +218,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchTargetMC() { TargetRegistry::RegisterELFStreamer(*T, createLoongArchELFStreamer); TargetRegistry::RegisterObjectTargetStreamer( *T, createLoongArchObjectTargetStreamer); + TargetRegistry::RegisterAsmTargetStreamer(*T, + createLoongArchAsmTargetStreamer); } } diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp index 57117844205519..8f9f0cb593b93b 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp @@ -22,3 +22,29 @@ void LoongArchTargetStreamer::setTargetABI(LoongArchABI::ABI ABI) { "Improperly initialized target ABI"); TargetABI = ABI; } + +void LoongArchTargetStreamer::emitDirectiveOptionPush() {} +void LoongArchTargetStreamer::emitDirectiveOptionPop() {} +void LoongArchTargetStreamer::emitDirectiveOptionRelax() {} +void LoongArchTargetStreamer::emitDirectiveOptionNoRelax() {} + +// This part is for ascii assembly output. +LoongArchTargetAsmStreamer::LoongArchTargetAsmStreamer( + MCStreamer &S, formatted_raw_ostream &OS) + : LoongArchTargetStreamer(S), OS(OS) {} + +void LoongArchTargetAsmStreamer::emitDirectiveOptionPush() { + OS << "\t.option\tpush\n"; +} + +void LoongArchTargetAsmStreamer::emitDirectiveOptionPop() { + OS << "\t.option\tpop\n"; +} + +void LoongArchTargetAsmStreamer::emitDirectiveOptionRelax() { + OS << "\t.option\trelax\n"; +} + +void LoongArchTargetAsmStreamer::emitDirectiveOptionNoRelax() { + OS << "\t.option\tnorelax\n"; +} diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h index d4b1b2a3e358db..96c324a3b6dc3c 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h @@ -12,6 +12,7 @@ #include "LoongArch.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormattedStream.h" namespace llvm { class LoongArchTargetStreamer : public MCTargetStreamer { @@ -21,6 +22,24 @@ class LoongArchTargetStreamer : public MCTargetStreamer { LoongArchTargetStreamer(MCStreamer &S); void setTargetABI(LoongArchABI::ABI ABI); LoongArchABI::ABI getTargetABI() const { return TargetABI; } + + virtual void emitDirectiveOptionPush(); + virtual void emitDirectiveOptionPop(); + virtual void emitDirectiveOptionRelax(); + virtual void emitDirectiveOptionNoRelax(); +}; + +// This part is for ascii assembly output. +class LoongArchTargetAsmStreamer : public LoongArchTargetStreamer { + formatted_raw_ostream &OS; + +public: + LoongArchTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + + void emitDirectiveOptionPush() override; + void emitDirectiveOptionPop() override; + void emitDirectiveOptionRelax() override; + void emitDirectiveOptionNoRelax() override; }; } // end namespace llvm diff --git a/llvm/test/MC/LoongArch/Directives/option-invalid.s b/llvm/test/MC/LoongArch/Directives/option-invalid.s new file mode 100644 index 00000000000000..d9064f03015452 --- /dev/null +++ b/llvm/test/MC/LoongArch/Directives/option-invalid.s @@ -0,0 +1,23 @@ +# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 \ +# RUN: | FileCheck --implicit-check-not=error: %s + +# CHECK: :[[#@LINE+1]]:8: error: expected identifier +.option + +# CHECK: :[[#@LINE+1]]:9: error: expected identifier +.option 123 + +# CHECK: :[[#@LINE+1]]:9: error: expected identifier +.option "str" + +# CHECK: :[[#@LINE+1]]:12: warning: unknown option, expected 'push', 'pop', 'relax' or 'norelax' +.option bar + +# CHECK: :[[#@LINE+1]]:12: error: .option pop with no .option push +.option pop + +# CHECK: :[[#@LINE+1]]:14: error: expected newline +.option push 123 + +# CHECK: :[[#@LINE+1]]:13: error: expected newline +.option pop 123 diff --git a/llvm/test/MC/LoongArch/Directives/option-pushpop.s b/llvm/test/MC/LoongArch/Directives/option-pushpop.s new file mode 100644 index 00000000000000..a33b7b8d1a09b3 --- /dev/null +++ b/llvm/test/MC/LoongArch/Directives/option-pushpop.s @@ -0,0 +1,59 @@ +# RUN: llvm-mc --triple=loongarch64 --mattr=-relax %s \ +# RUN: | FileCheck --check-prefix=CHECK-ASM %s +# RUN: llvm-mc --triple=loongarch64 --mattr=-relax --filetype=obj %s \ +# RUN: | llvm-readobj -r - | FileCheck --check-prefix=CHECK-RELOC %s + +## Test the operation of the push and pop assembler directives when +## using .option relax. Checks that using .option pop correctly restores +## all target features to their state at the point where .option pop was +## last used. + +# CHECK-ASM: .option push +.option push # relax = false + +# CHECK-ASM: .option relax +.option relax # relax = true + +# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym1) +# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym1) +# CHECK-RELOC: R_LARCH_PCALA_HI20 sym1 0x0 +# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0 +# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym1 0x0 +# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0 +la.pcrel $a0, sym1 + +# CHECK-ASM: .option push +.option push # relax = true + +# CHECK-ASM: .option norelax +.option norelax # relax = false + +# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym2) +# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym2) +# CHECK-RELOC-NEXT: R_LARCH_PCALA_HI20 sym2 0x0 +# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0 +# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym2 0x0 +# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0 +la.pcrel $a0, sym2 + +# CHECK-ASM: .option pop +.option pop # relax = true + +# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym3) +# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym3) +# CHECK-RELOC: R_LARCH_PCALA_HI20 sym3 0x0 +# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0 +# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym3 0x0 +# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0 +la.pcrel $a0, sym3 + +# CHECK-ASM: .option pop +.option pop # relax = false + +la.pcrel $a0, sym4 +# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym4) +# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym4) +# CHECK-RELOC-NEXT: R_LARCH_PCALA_HI20 sym4 0x0 +# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0 +# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym4 0x0 +# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0 diff --git a/llvm/test/MC/LoongArch/Directives/option-relax.s b/llvm/test/MC/LoongArch/Directives/option-relax.s new file mode 100644 index 00000000000000..587e1833085622 --- /dev/null +++ b/llvm/test/MC/LoongArch/Directives/option-relax.s @@ -0,0 +1,30 @@ +# RUN: llvm-mc --triple=loongarch64 %s | FileCheck --check-prefix=CHECK-ASM %s +# RUN: llvm-mc -filetype=obj --triple=loongarch64 %s \ +# RUN: | llvm-readobj -r - | FileCheck -check-prefix=CHECK-RELOC %s + +## Check .option relax causes R_LARCH_RELAX to be emitted, and .option +## norelax suppresses it. + +# CHECK-ASM: .option relax +.option relax + +# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym1) +# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym1) + +# CHECK-RELOC: R_LARCH_PCALA_HI20 sym1 0x0 +# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0 +# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym1 0x0 +# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0 +la.pcrel $a0, sym1 + +# CHECK-ASM: .option norelax +.option norelax + +# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym2) +# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym2) + +# CHECK-RELOC-NEXT: R_LARCH_PCALA_HI20 sym2 0x0 +# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0 +# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym2 0x0 +# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0 +la.pcrel $a0, sym2