Skip to content

[LoongArch] Support .option directive #110404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

wangleiat
Copy link
Contributor

The .option can accept 4 parameters like the LoongArch's gnu as:
push, pop, relax and norelax.

Created using spr 1.3.5-bogner
@llvmbot llvmbot added mc Machine (object) code backend:loongarch labels Sep 29, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 29, 2024

@llvm/pr-subscribers-backend-loongarch

Author: wanglei (wangleiat)

Changes

The .option can accept 4 parameters like the LoongArch's gnu as:
push, pop, relax and norelax.


Full diff: https://github.com/llvm/llvm-project/pull/110404.diff

8 Files Affected:

  • (modified) llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp (+108)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.cpp (+5)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h (+5)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp (+8)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp (+26)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h (+19)
  • (added) llvm/test/MC/LoongArch/Directives/option-pushpop.s (+59)
  • (added) llvm/test/MC/LoongArch/Directives/option-relax.s (+30)
diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 57c42024b4d2b2..d9752e7951a951 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<FeatureBitset, 4> 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<LoongArchTargetStreamer &>(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<LoongArchAsmParser> X(getTheLoongArch32Target());
   RegisterMCAsmParser<LoongArchAsmParser> 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<MCELFStreamer &>(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-pushpop.s b/llvm/test/MC/LoongArch/Directives/option-pushpop.s
new file mode 100644
index 00000000000000..fbc7ea09b2fe72
--- /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 --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..e016e47156d39e
--- /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

@llvmbot
Copy link
Member

llvmbot commented Sep 29, 2024

@llvm/pr-subscribers-mc

Author: wanglei (wangleiat)

Changes

The .option can accept 4 parameters like the LoongArch's gnu as:
push, pop, relax and norelax.


Full diff: https://github.com/llvm/llvm-project/pull/110404.diff

8 Files Affected:

  • (modified) llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp (+108)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.cpp (+5)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h (+5)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp (+8)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp (+26)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h (+19)
  • (added) llvm/test/MC/LoongArch/Directives/option-pushpop.s (+59)
  • (added) llvm/test/MC/LoongArch/Directives/option-relax.s (+30)
diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 57c42024b4d2b2..d9752e7951a951 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<FeatureBitset, 4> 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<LoongArchTargetStreamer &>(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<LoongArchAsmParser> X(getTheLoongArch32Target());
   RegisterMCAsmParser<LoongArchAsmParser> 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<MCELFStreamer &>(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-pushpop.s b/llvm/test/MC/LoongArch/Directives/option-pushpop.s
new file mode 100644
index 00000000000000..fbc7ea09b2fe72
--- /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 --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..e016e47156d39e
--- /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

Created using spr 1.3.5-bogner
@SixWeining SixWeining requested a review from heiher October 7, 2024 08:01
Created using spr 1.3.5-bogner
Copy link
Member

@heiher heiher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks.

@wangleiat wangleiat merged commit 1a787b3 into main Oct 14, 2024
9 checks passed
@wangleiat wangleiat deleted the users/wangleiat/spr/loongarch-support-option-directive branch October 14, 2024 03:45
DanielCChen pushed a commit to DanielCChen/llvm-project that referenced this pull request Oct 16, 2024
The .option can accept 4 parameters like the LoongArch's gnu as:
push, pop, relax and norelax.

Reviewed By: heiher, SixWeining

Pull Request: llvm#110404
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:loongarch mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants