[llvm] [LoongArch] Support .option directive (PR #110404)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 28 21:09:34 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-loongarch

Author: wanglei (wangleiat)

<details>
<summary>Changes</summary>

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
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

``````````

</details>


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


More information about the llvm-commits mailing list