Skip to content

[RISCV] Support .option {no}exact #122483

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
merged 21 commits into from
Mar 26, 2025
Merged

Conversation

lenary
Copy link
Member

@lenary lenary commented Jan 10, 2025

This implements the .option exact and .option noexact proposal for RISC-V.

.option exact turns off:

  • Compression
  • Branch Relaxation
  • Linker Relaxation

.option noexact turns these back on, and is also the default, matching the current behaviour.

This is the implementation of a new assembler-only option to allow the
automatic compression of RISC-V instructions to be switched off without
changing the currently enabled architectural features.

This allows users better control over the autocompression feature of the
assembler, so they can get the exact instructions they want.

This will become more useful as the following things happen:
- `.option norvc` is deprecated/removed (which is sometimes used for
  this purpose).
- Extensions are added where the destination instruction cannot be
  disabled separately to the source instruction, either because the
  destination is in the base architecture, or because it is in the same
  extension as the source.
- Extensions wider than 32-bits are added, which make CompressPats more
  complex to use intuitively, especially if the destination is a 32-bit
  instruction.
@lenary
Copy link
Member Author

lenary commented Jan 10, 2025

The assembly manual proposal is here: riscv-non-isa/riscv-asm-manual#122

@topperc
Copy link
Collaborator

topperc commented Jan 10, 2025

Thanks for doing this. Kito and I have talked about something similar in the past. .option rvc has been broken since Zca/Zcf/Zcd was introduced.

@lenary
Copy link
Member Author

lenary commented Jan 10, 2025

Thanks for doing this. Kito and I have talked about something similar in the past. .option rvc has been broken since Zca/Zcf/Zcd was introduced.

Yeah. Just like EF_RVC conflates two things (C extension being present and IALIGN=16), .option norvc (effectively) conflates two things (C extension being present, and compression patterns being enabled). This is part of the missing work I mentioned that prevented us adding CompressPats to the Xqci work - the other is the tied operand issue that Sudharsan mentioned.

@lenary lenary changed the title [RISCV] Support .option (no)autocompress [RISCV] Support .option (no)exact Mar 18, 2025
@lenary lenary changed the title [RISCV] Support .option (no)exact [RISCV] Support .option {no}exact Mar 18, 2025
Copy link

github-actions bot commented Mar 19, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@lenary lenary marked this pull request as ready for review March 19, 2025 20:57
@llvmbot llvmbot added backend:RISC-V mc Machine (object) code labels Mar 19, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 19, 2025

@llvm/pr-subscribers-mc

Author: Sam Elliott (lenary)

Changes

This implements the .option exact and .option noexact proposal for RISC-V.

.option exact turns off:

  • Compression
  • Branch Relaxation
  • Linker Relaxation

.option noexact turns these back on, and is also the default, matching the current behaviour.


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

10 Files Affected:

  • (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+29-5)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (+10)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp (+10)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h (+4)
  • (modified) llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp (+3-1)
  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+4)
  • (added) llvm/test/MC/RISCV/option-exact-compression.s (+72)
  • (added) llvm/test/MC/RISCV/option-exact-relaxation.s (+74)
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index ce25380cbdda2..8dd428a1ba461 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -3173,6 +3173,26 @@ bool RISCVAsmParser::parseDirectiveOption() {
     return false;
   }
 
+  if (Option == "exact") {
+    if (Parser.parseEOL())
+      return true;
+
+    getTargetStreamer().emitDirectiveOptionExact();
+    setFeatureBits(RISCV::FeatureExactAssembly, "exact-asm");
+    clearFeatureBits(RISCV::FeatureRelax, "relax");
+    return false;
+  }
+
+  if (Option == "noexact") {
+    if (Parser.parseEOL())
+      return true;
+
+    getTargetStreamer().emitDirectiveOptionNoExact();
+    clearFeatureBits(RISCV::FeatureExactAssembly, "exact-asm");
+    setFeatureBits(RISCV::FeatureRelax, "relax");
+    return false;
+  }
+
   if (Option == "rvc") {
     if (Parser.parseEOL())
       return true;
@@ -3229,9 +3249,10 @@ bool RISCVAsmParser::parseDirectiveOption() {
   }
 
   // Unknown option.
-  Warning(Parser.getTok().getLoc(), "unknown option, expected 'push', 'pop', "
-                                    "'rvc', 'norvc', 'arch', 'relax' or "
-                                    "'norelax'");
+  Warning(Parser.getTok().getLoc(),
+          "unknown option, expected 'push', 'pop', "
+          "'rvc', 'norvc', 'arch', 'relax', 'norelax', "
+          "'exact' or 'noexact'");
   Parser.eatToEndOfStatement();
   return false;
 }
@@ -3441,10 +3462,13 @@ bool RISCVAsmParser::parseDirectiveVariantCC() {
 
 void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) {
   MCInst CInst;
-  bool Res = RISCVRVC::compress(CInst, Inst, getSTI());
+  bool Res = false;
+  const MCSubtargetInfo &STI = getSTI();
+  if (!STI.hasFeature(RISCV::FeatureExactAssembly))
+    Res = RISCVRVC::compress(CInst, Inst, STI);
   if (Res)
     ++RISCVNumInstrsCompressed;
-  S.emitInstruction((Res ? CInst : Inst), getSTI());
+  S.emitInstruction((Res ? CInst : Inst), STI);
 }
 
 void RISCVAsmParser::emitLoadImm(MCRegister DestReg, int64_t Value,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index e752b0ec5f58c..83e6f7169e957 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -344,6 +344,10 @@ std::pair<bool, bool> RISCVAsmBackend::relaxLEB128(const MCAssembler &Asm,
 // Given a compressed control flow instruction this function returns
 // the expanded instruction.
 unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
+  // Disable relaxation if FeatureExactAssembly
+  if (STI.hasFeature(RISCV::FeatureExactAssembly))
+    return Op;
+
   switch (Op) {
   default:
     return Op;
@@ -371,6 +375,12 @@ unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
 
 bool RISCVAsmBackend::mayNeedRelaxation(const MCInst &Inst,
                                         const MCSubtargetInfo &STI) const {
+  // This function has access to two STIs, the member of the AsmBackend, and the
+  // one passed as an argument. The latter is more specific, so we query it for
+  // specific features.
+  if (STI.hasFeature(RISCV::FeatureExactAssembly))
+    return false;
+
   return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode();
 }
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 9eabc61331832..1814ca2a077f3 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -53,6 +53,8 @@ void RISCVTargetELFStreamer::emitDirectiveOptionPIC() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionNoPIC() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {}
+void RISCVTargetELFStreamer::emitDirectiveOptionExact() {}
+void RISCVTargetELFStreamer::emitDirectiveOptionNoExact() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {}
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
index e1765805b4a26..bf84f8e700e6d 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
@@ -62,6 +62,8 @@ class RISCVTargetELFStreamer : public RISCVTargetStreamer {
   void emitDirectiveOptionNoPIC() override;
   void emitDirectiveOptionRVC() override;
   void emitDirectiveOptionNoRVC() override;
+  void emitDirectiveOptionExact() override;
+  void emitDirectiveOptionNoExact() override;
   void emitDirectiveOptionRelax() override;
   void emitDirectiveOptionNoRelax() override;
   void emitDirectiveVariantCC(MCSymbol &Symbol) override;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
index ce64c61034901..04a0feb98eec2 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
@@ -39,6 +39,8 @@ void RISCVTargetStreamer::emitDirectiveOptionPIC() {}
 void RISCVTargetStreamer::emitDirectiveOptionNoPIC() {}
 void RISCVTargetStreamer::emitDirectiveOptionRVC() {}
 void RISCVTargetStreamer::emitDirectiveOptionNoRVC() {}
+void RISCVTargetStreamer::emitDirectiveOptionExact() {}
+void RISCVTargetStreamer::emitDirectiveOptionNoExact() {}
 void RISCVTargetStreamer::emitDirectiveOptionRelax() {}
 void RISCVTargetStreamer::emitDirectiveOptionNoRelax() {}
 void RISCVTargetStreamer::emitDirectiveOptionArch(
@@ -125,6 +127,14 @@ void RISCVTargetAsmStreamer::emitDirectiveOptionNoRVC() {
   OS << "\t.option\tnorvc\n";
 }
 
+void RISCVTargetAsmStreamer::emitDirectiveOptionExact() {
+  OS << "\t.option\texact\n";
+}
+
+void RISCVTargetAsmStreamer::emitDirectiveOptionNoExact() {
+  OS << "\t.option\tnoexact\n";
+}
+
 void RISCVTargetAsmStreamer::emitDirectiveOptionRelax() {
   OS << "\t.option\trelax\n";
 }
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
index cb8bc21cb6355..a23ac19c0cabb 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
@@ -47,6 +47,8 @@ class RISCVTargetStreamer : public MCTargetStreamer {
   virtual void emitDirectiveOptionNoPIC();
   virtual void emitDirectiveOptionRVC();
   virtual void emitDirectiveOptionNoRVC();
+  virtual void emitDirectiveOptionExact();
+  virtual void emitDirectiveOptionNoExact();
   virtual void emitDirectiveOptionRelax();
   virtual void emitDirectiveOptionNoRelax();
   virtual void emitDirectiveOptionArch(ArrayRef<RISCVOptionArchArg> Args);
@@ -84,6 +86,8 @@ class RISCVTargetAsmStreamer : public RISCVTargetStreamer {
   void emitDirectiveOptionNoPIC() override;
   void emitDirectiveOptionRVC() override;
   void emitDirectiveOptionNoRVC() override;
+  void emitDirectiveOptionExact() override;
+  void emitDirectiveOptionNoExact() override;
   void emitDirectiveOptionRelax() override;
   void emitDirectiveOptionNoRelax() override;
   void emitDirectiveOptionArch(ArrayRef<RISCVOptionArchArg> Args) override;
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index 12519c86bf44b..225b9fb88cdaf 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -254,7 +254,9 @@ void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
 bool RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst,
                                      const MCSubtargetInfo &SubtargetInfo) {
   MCInst CInst;
-  bool Res = RISCVRVC::compress(CInst, Inst, SubtargetInfo);
+  bool Res = false;
+  if (!SubtargetInfo.hasFeature(RISCV::FeatureExactAssembly))
+    Res = RISCVRVC::compress(CInst, Inst, SubtargetInfo);
   if (Res)
     ++RISCVNumInstrsCompressed;
   S.emitInstruction(Res ? CInst : Inst, SubtargetInfo);
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index f23a855e7049f..57029b971193a 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1470,6 +1470,10 @@ def FeatureRelax
     : SubtargetFeature<"relax", "EnableLinkerRelax", "true",
                        "Enable Linker relaxation.">;
 
+def FeatureExactAssembly
+    : SubtargetFeature<"exact-asm", "EnableExactAssembly", "true",
+                       "Enable Exact Assembly (Disables Compression and Relaxation)">;
+
 foreach i = {1-31} in
   def FeatureReserveX#i :
       SubtargetFeature<"reserve-x"#i, "UserReservedRegister[RISCV::X"#i#"]",
diff --git a/llvm/test/MC/RISCV/option-exact-compression.s b/llvm/test/MC/RISCV/option-exact-compression.s
new file mode 100644
index 0000000000000..ae38bf6647d17
--- /dev/null
+++ b/llvm/test/MC/RISCV/option-exact-compression.s
@@ -0,0 +1,72 @@
+# RUN: llvm-mc -triple riscv32 -show-encoding -mattr=+c %s \
+# RUN:   | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv32 -show-encoding -mattr=+c \
+# RUN:   -M no-aliases %s | FileCheck -check-prefixes=CHECK,CHECK-INST %s
+# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv32 --mattr=+c --no-print-imm-hex -d - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv32 --mattr=+c --no-print-imm-hex -d -M no-aliases - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s
+
+# RUN: llvm-mc -triple riscv64 -show-encoding -mattr=+c %s \
+# RUN:   | FileCheck -check-prefixes=CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv64 -show-encoding -mattr=+c \
+# RUN:   -M no-aliases %s | FileCheck -check-prefixes=CHECK-INST %s
+# RUN: llvm-mc -triple riscv64 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv64 --mattr=+c --no-print-imm-hex -d - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv64 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv64 --mattr=+c --no-print-imm-hex -d -M no-aliases - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s
+
+
+## `.option exact` disables a variety of assembler behaviour:
+## - automatic compression
+## - branch relaxation (of short branches to longer equivalent sequences)
+## - linker relaxation (emitting R_RISCV_RELAX)
+## `.option noexact` enables these behaviours again. It is also the default.
+
+## This test only checks the automatic compression part of this behaviour.
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+lw a0, 0(a0)
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+c.lw a0, 0(a0)
+
+# CHECK: .option exact
+.option exact
+
+# CHECK-BYTES: 00052503
+# CHECK-INST: lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x03,0x25,0x05,0x00]
+lw a0, 0(a0)
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+c.lw a0, 0(a0)
+
+# CHECK: .option noexact
+.option noexact
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+lw a0, 0(a0)
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+c.lw a0, 0(a0)
diff --git a/llvm/test/MC/RISCV/option-exact-relaxation.s b/llvm/test/MC/RISCV/option-exact-relaxation.s
new file mode 100644
index 0000000000000..ed0a18290c67d
--- /dev/null
+++ b/llvm/test/MC/RISCV/option-exact-relaxation.s
@@ -0,0 +1,74 @@
+# RUN: llvm-mc -triple riscv32 -show-encoding -mattr=+relax %s \
+# RUN:   | FileCheck -check-prefixes=CHECK-ASM %s
+# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+relax %s \
+# RUN:   | llvm-objdump  --triple=riscv32 --no-show-raw-insn -dr - \
+# RUN:   | FileCheck -check-prefixes=CHECK-OBJDUMP %s
+
+# RUN: llvm-mc -triple riscv64 -show-encoding -mattr=+relax %s \
+# RUN:   | FileCheck -check-prefixes=CHECK-ASM %s
+# RUN: llvm-mc -triple riscv64 -filetype=obj -mattr=+relax %s \
+# RUN:   | llvm-objdump  --triple=riscv64 --no-show-raw-insn -dr - \
+# RUN:   | FileCheck -check-prefixes=CHECK-OBJDUMP %s
+
+## `.option exact` disables a variety of assembler behaviour:
+## - automatic compression
+## - branch relaxation (of short branches to longer equivalent sequences)
+## - linker relaxation (emitting R_RISCV_RELAX)
+## `.option noexact` enables these behaviours again. It is also the default.
+
+## This test only checks the branch and linker relaxation part of this behaviour.
+
+
+
+# CHECK-ASM: call undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_call_plt
+# CHECK-ASM-NEXT: fixup B - offset: 0, value: 0, kind: fixup_riscv_relax
+# CHECK-OBJDUMP: auipc ra, 0x0
+# CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
+# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-OBJDUMP-NEXT: jalr ra
+call undefined@plt
+
+# CHECK-ASM: beq a0, a1, undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_branch
+# CHECK-OBJDUMP: bne a0, a1, 0x10
+# CHECK-OBJDUMP-NEXT: j 0xc
+# CHECK-OBJDUMP-NEXT: R_RISCV_JAL undefined
+beq a0, a1, undefined
+
+# CHECK-ASM: .option exact
+.option exact
+
+# CHECK-ASM: call undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_call_plt
+# CHECK-ASM-NOT: fixup_riscv_relax
+# CHECK-OBJDUMP: auipc ra, 0x0
+# CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
+# CHECK-OBJDUMP-NOT: R_RISCV_RELAX
+# CHECK-OBJDUMP-NEXT: jalr ra
+call undefined@plt
+
+# CHECK-ASM: beq a0, a1, undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_branch
+# CHECK-OBJDUMP: beq a0, a1, 0x18
+# CHECK-OBJDUMP-NEXT: R_RISCV_BRANCH undefined
+beq a0, a1, undefined
+
+# CHECK-ASM: .option noexact
+.option noexact
+
+# CHECK-ASM: call undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_call_plt
+# CHECK-ASM-NEXT: fixup B - offset: 0, value: 0, kind: fixup_riscv_relax
+# CHECK-OBJDUMP: auipc ra, 0x0
+# CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
+# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-OBJDUMP-NEXT: jalr ra
+call undefined@plt
+
+# CHECK-ASM: beq a0, a1, undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_branch
+# CHECK-OBJDUMP: bne a0, a1, 0x2c
+# CHECK-OBJDUMP-NEXT: j 0x28
+# CHECK-OBJDUMP-NEXT: R_RISCV_JAL undefined
+beq a0, a1, undefined

@llvmbot
Copy link
Member

llvmbot commented Mar 19, 2025

@llvm/pr-subscribers-backend-risc-v

Author: Sam Elliott (lenary)

Changes

This implements the .option exact and .option noexact proposal for RISC-V.

.option exact turns off:

  • Compression
  • Branch Relaxation
  • Linker Relaxation

.option noexact turns these back on, and is also the default, matching the current behaviour.


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

10 Files Affected:

  • (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+29-5)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (+10)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp (+10)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h (+4)
  • (modified) llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp (+3-1)
  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+4)
  • (added) llvm/test/MC/RISCV/option-exact-compression.s (+72)
  • (added) llvm/test/MC/RISCV/option-exact-relaxation.s (+74)
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index ce25380cbdda2..8dd428a1ba461 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -3173,6 +3173,26 @@ bool RISCVAsmParser::parseDirectiveOption() {
     return false;
   }
 
+  if (Option == "exact") {
+    if (Parser.parseEOL())
+      return true;
+
+    getTargetStreamer().emitDirectiveOptionExact();
+    setFeatureBits(RISCV::FeatureExactAssembly, "exact-asm");
+    clearFeatureBits(RISCV::FeatureRelax, "relax");
+    return false;
+  }
+
+  if (Option == "noexact") {
+    if (Parser.parseEOL())
+      return true;
+
+    getTargetStreamer().emitDirectiveOptionNoExact();
+    clearFeatureBits(RISCV::FeatureExactAssembly, "exact-asm");
+    setFeatureBits(RISCV::FeatureRelax, "relax");
+    return false;
+  }
+
   if (Option == "rvc") {
     if (Parser.parseEOL())
       return true;
@@ -3229,9 +3249,10 @@ bool RISCVAsmParser::parseDirectiveOption() {
   }
 
   // Unknown option.
-  Warning(Parser.getTok().getLoc(), "unknown option, expected 'push', 'pop', "
-                                    "'rvc', 'norvc', 'arch', 'relax' or "
-                                    "'norelax'");
+  Warning(Parser.getTok().getLoc(),
+          "unknown option, expected 'push', 'pop', "
+          "'rvc', 'norvc', 'arch', 'relax', 'norelax', "
+          "'exact' or 'noexact'");
   Parser.eatToEndOfStatement();
   return false;
 }
@@ -3441,10 +3462,13 @@ bool RISCVAsmParser::parseDirectiveVariantCC() {
 
 void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) {
   MCInst CInst;
-  bool Res = RISCVRVC::compress(CInst, Inst, getSTI());
+  bool Res = false;
+  const MCSubtargetInfo &STI = getSTI();
+  if (!STI.hasFeature(RISCV::FeatureExactAssembly))
+    Res = RISCVRVC::compress(CInst, Inst, STI);
   if (Res)
     ++RISCVNumInstrsCompressed;
-  S.emitInstruction((Res ? CInst : Inst), getSTI());
+  S.emitInstruction((Res ? CInst : Inst), STI);
 }
 
 void RISCVAsmParser::emitLoadImm(MCRegister DestReg, int64_t Value,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index e752b0ec5f58c..83e6f7169e957 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -344,6 +344,10 @@ std::pair<bool, bool> RISCVAsmBackend::relaxLEB128(const MCAssembler &Asm,
 // Given a compressed control flow instruction this function returns
 // the expanded instruction.
 unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
+  // Disable relaxation if FeatureExactAssembly
+  if (STI.hasFeature(RISCV::FeatureExactAssembly))
+    return Op;
+
   switch (Op) {
   default:
     return Op;
@@ -371,6 +375,12 @@ unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
 
 bool RISCVAsmBackend::mayNeedRelaxation(const MCInst &Inst,
                                         const MCSubtargetInfo &STI) const {
+  // This function has access to two STIs, the member of the AsmBackend, and the
+  // one passed as an argument. The latter is more specific, so we query it for
+  // specific features.
+  if (STI.hasFeature(RISCV::FeatureExactAssembly))
+    return false;
+
   return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode();
 }
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 9eabc61331832..1814ca2a077f3 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -53,6 +53,8 @@ void RISCVTargetELFStreamer::emitDirectiveOptionPIC() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionNoPIC() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {}
+void RISCVTargetELFStreamer::emitDirectiveOptionExact() {}
+void RISCVTargetELFStreamer::emitDirectiveOptionNoExact() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {}
 void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {}
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
index e1765805b4a26..bf84f8e700e6d 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
@@ -62,6 +62,8 @@ class RISCVTargetELFStreamer : public RISCVTargetStreamer {
   void emitDirectiveOptionNoPIC() override;
   void emitDirectiveOptionRVC() override;
   void emitDirectiveOptionNoRVC() override;
+  void emitDirectiveOptionExact() override;
+  void emitDirectiveOptionNoExact() override;
   void emitDirectiveOptionRelax() override;
   void emitDirectiveOptionNoRelax() override;
   void emitDirectiveVariantCC(MCSymbol &Symbol) override;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
index ce64c61034901..04a0feb98eec2 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
@@ -39,6 +39,8 @@ void RISCVTargetStreamer::emitDirectiveOptionPIC() {}
 void RISCVTargetStreamer::emitDirectiveOptionNoPIC() {}
 void RISCVTargetStreamer::emitDirectiveOptionRVC() {}
 void RISCVTargetStreamer::emitDirectiveOptionNoRVC() {}
+void RISCVTargetStreamer::emitDirectiveOptionExact() {}
+void RISCVTargetStreamer::emitDirectiveOptionNoExact() {}
 void RISCVTargetStreamer::emitDirectiveOptionRelax() {}
 void RISCVTargetStreamer::emitDirectiveOptionNoRelax() {}
 void RISCVTargetStreamer::emitDirectiveOptionArch(
@@ -125,6 +127,14 @@ void RISCVTargetAsmStreamer::emitDirectiveOptionNoRVC() {
   OS << "\t.option\tnorvc\n";
 }
 
+void RISCVTargetAsmStreamer::emitDirectiveOptionExact() {
+  OS << "\t.option\texact\n";
+}
+
+void RISCVTargetAsmStreamer::emitDirectiveOptionNoExact() {
+  OS << "\t.option\tnoexact\n";
+}
+
 void RISCVTargetAsmStreamer::emitDirectiveOptionRelax() {
   OS << "\t.option\trelax\n";
 }
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
index cb8bc21cb6355..a23ac19c0cabb 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
@@ -47,6 +47,8 @@ class RISCVTargetStreamer : public MCTargetStreamer {
   virtual void emitDirectiveOptionNoPIC();
   virtual void emitDirectiveOptionRVC();
   virtual void emitDirectiveOptionNoRVC();
+  virtual void emitDirectiveOptionExact();
+  virtual void emitDirectiveOptionNoExact();
   virtual void emitDirectiveOptionRelax();
   virtual void emitDirectiveOptionNoRelax();
   virtual void emitDirectiveOptionArch(ArrayRef<RISCVOptionArchArg> Args);
@@ -84,6 +86,8 @@ class RISCVTargetAsmStreamer : public RISCVTargetStreamer {
   void emitDirectiveOptionNoPIC() override;
   void emitDirectiveOptionRVC() override;
   void emitDirectiveOptionNoRVC() override;
+  void emitDirectiveOptionExact() override;
+  void emitDirectiveOptionNoExact() override;
   void emitDirectiveOptionRelax() override;
   void emitDirectiveOptionNoRelax() override;
   void emitDirectiveOptionArch(ArrayRef<RISCVOptionArchArg> Args) override;
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index 12519c86bf44b..225b9fb88cdaf 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -254,7 +254,9 @@ void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
 bool RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst,
                                      const MCSubtargetInfo &SubtargetInfo) {
   MCInst CInst;
-  bool Res = RISCVRVC::compress(CInst, Inst, SubtargetInfo);
+  bool Res = false;
+  if (!SubtargetInfo.hasFeature(RISCV::FeatureExactAssembly))
+    Res = RISCVRVC::compress(CInst, Inst, SubtargetInfo);
   if (Res)
     ++RISCVNumInstrsCompressed;
   S.emitInstruction(Res ? CInst : Inst, SubtargetInfo);
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index f23a855e7049f..57029b971193a 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1470,6 +1470,10 @@ def FeatureRelax
     : SubtargetFeature<"relax", "EnableLinkerRelax", "true",
                        "Enable Linker relaxation.">;
 
+def FeatureExactAssembly
+    : SubtargetFeature<"exact-asm", "EnableExactAssembly", "true",
+                       "Enable Exact Assembly (Disables Compression and Relaxation)">;
+
 foreach i = {1-31} in
   def FeatureReserveX#i :
       SubtargetFeature<"reserve-x"#i, "UserReservedRegister[RISCV::X"#i#"]",
diff --git a/llvm/test/MC/RISCV/option-exact-compression.s b/llvm/test/MC/RISCV/option-exact-compression.s
new file mode 100644
index 0000000000000..ae38bf6647d17
--- /dev/null
+++ b/llvm/test/MC/RISCV/option-exact-compression.s
@@ -0,0 +1,72 @@
+# RUN: llvm-mc -triple riscv32 -show-encoding -mattr=+c %s \
+# RUN:   | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv32 -show-encoding -mattr=+c \
+# RUN:   -M no-aliases %s | FileCheck -check-prefixes=CHECK,CHECK-INST %s
+# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv32 --mattr=+c --no-print-imm-hex -d - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv32 --mattr=+c --no-print-imm-hex -d -M no-aliases - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s
+
+# RUN: llvm-mc -triple riscv64 -show-encoding -mattr=+c %s \
+# RUN:   | FileCheck -check-prefixes=CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv64 -show-encoding -mattr=+c \
+# RUN:   -M no-aliases %s | FileCheck -check-prefixes=CHECK-INST %s
+# RUN: llvm-mc -triple riscv64 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv64 --mattr=+c --no-print-imm-hex -d - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s
+# RUN: llvm-mc -triple riscv64 -filetype=obj -mattr=+c %s \
+# RUN:   | llvm-objdump  --triple=riscv64 --mattr=+c --no-print-imm-hex -d -M no-aliases - \
+# RUN:   | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s
+
+
+## `.option exact` disables a variety of assembler behaviour:
+## - automatic compression
+## - branch relaxation (of short branches to longer equivalent sequences)
+## - linker relaxation (emitting R_RISCV_RELAX)
+## `.option noexact` enables these behaviours again. It is also the default.
+
+## This test only checks the automatic compression part of this behaviour.
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+lw a0, 0(a0)
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+c.lw a0, 0(a0)
+
+# CHECK: .option exact
+.option exact
+
+# CHECK-BYTES: 00052503
+# CHECK-INST: lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x03,0x25,0x05,0x00]
+lw a0, 0(a0)
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+c.lw a0, 0(a0)
+
+# CHECK: .option noexact
+.option noexact
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+lw a0, 0(a0)
+
+# CHECK-BYTES: 4108
+# CHECK-INST: c.lw a0, 0(a0)
+# CHECK-ALIAS: lw a0, 0(a0)
+# CHECK: # encoding: [0x08,0x41]
+c.lw a0, 0(a0)
diff --git a/llvm/test/MC/RISCV/option-exact-relaxation.s b/llvm/test/MC/RISCV/option-exact-relaxation.s
new file mode 100644
index 0000000000000..ed0a18290c67d
--- /dev/null
+++ b/llvm/test/MC/RISCV/option-exact-relaxation.s
@@ -0,0 +1,74 @@
+# RUN: llvm-mc -triple riscv32 -show-encoding -mattr=+relax %s \
+# RUN:   | FileCheck -check-prefixes=CHECK-ASM %s
+# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+relax %s \
+# RUN:   | llvm-objdump  --triple=riscv32 --no-show-raw-insn -dr - \
+# RUN:   | FileCheck -check-prefixes=CHECK-OBJDUMP %s
+
+# RUN: llvm-mc -triple riscv64 -show-encoding -mattr=+relax %s \
+# RUN:   | FileCheck -check-prefixes=CHECK-ASM %s
+# RUN: llvm-mc -triple riscv64 -filetype=obj -mattr=+relax %s \
+# RUN:   | llvm-objdump  --triple=riscv64 --no-show-raw-insn -dr - \
+# RUN:   | FileCheck -check-prefixes=CHECK-OBJDUMP %s
+
+## `.option exact` disables a variety of assembler behaviour:
+## - automatic compression
+## - branch relaxation (of short branches to longer equivalent sequences)
+## - linker relaxation (emitting R_RISCV_RELAX)
+## `.option noexact` enables these behaviours again. It is also the default.
+
+## This test only checks the branch and linker relaxation part of this behaviour.
+
+
+
+# CHECK-ASM: call undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_call_plt
+# CHECK-ASM-NEXT: fixup B - offset: 0, value: 0, kind: fixup_riscv_relax
+# CHECK-OBJDUMP: auipc ra, 0x0
+# CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
+# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-OBJDUMP-NEXT: jalr ra
+call undefined@plt
+
+# CHECK-ASM: beq a0, a1, undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_branch
+# CHECK-OBJDUMP: bne a0, a1, 0x10
+# CHECK-OBJDUMP-NEXT: j 0xc
+# CHECK-OBJDUMP-NEXT: R_RISCV_JAL undefined
+beq a0, a1, undefined
+
+# CHECK-ASM: .option exact
+.option exact
+
+# CHECK-ASM: call undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_call_plt
+# CHECK-ASM-NOT: fixup_riscv_relax
+# CHECK-OBJDUMP: auipc ra, 0x0
+# CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
+# CHECK-OBJDUMP-NOT: R_RISCV_RELAX
+# CHECK-OBJDUMP-NEXT: jalr ra
+call undefined@plt
+
+# CHECK-ASM: beq a0, a1, undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_branch
+# CHECK-OBJDUMP: beq a0, a1, 0x18
+# CHECK-OBJDUMP-NEXT: R_RISCV_BRANCH undefined
+beq a0, a1, undefined
+
+# CHECK-ASM: .option noexact
+.option noexact
+
+# CHECK-ASM: call undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_call_plt
+# CHECK-ASM-NEXT: fixup B - offset: 0, value: 0, kind: fixup_riscv_relax
+# CHECK-OBJDUMP: auipc ra, 0x0
+# CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
+# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-OBJDUMP-NEXT: jalr ra
+call undefined@plt
+
+# CHECK-ASM: beq a0, a1, undefined
+# CHECK-ASM-NEXT: fixup A - offset: 0, value: undefined, kind: fixup_riscv_branch
+# CHECK-OBJDUMP: bne a0, a1, 0x2c
+# CHECK-OBJDUMP-NEXT: j 0x28
+# CHECK-OBJDUMP-NEXT: R_RISCV_JAL undefined
+beq a0, a1, undefined

@@ -254,7 +254,9 @@ void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
bool RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst,
const MCSubtargetInfo &SubtargetInfo) {
MCInst CInst;
bool Res = RISCVRVC::compress(CInst, Inst, SubtargetInfo);
bool Res = false;
if (!SubtargetInfo.hasFeature(RISCV::FeatureExactAssembly))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a test for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

No. I think this would need a LLC test - let me work out what to do here - I'm not sure which EmitToStreamer is used for inline assembly.

I think I only want to keep this if inline assembly hits it, otherwise I don't want to present this as something for compilation - exact mode is trying to just be for assembly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Inline assembly does not use this emit function, so I have removed this change.

@@ -344,6 +344,10 @@ std::pair<bool, bool> RISCVAsmBackend::relaxLEB128(const MCAssembler &Asm,
// Given a compressed control flow instruction this function returns
// the expanded instruction.
unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
// Disable relaxation if FeatureExactAssembly
if (STI.hasFeature(RISCV::FeatureExactAssembly))
Copy link
Collaborator

@topperc topperc Mar 19, 2025

Choose a reason for hiding this comment

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

This is a different STI than what mayNeedRelaxation is using right?

Should we check in relaxInstruction instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, this is indeed a different STI to mayNeedRelaxation.

I wasn't sure about changing relaxInstruction, because of the llvm_unreachable which suggested to me it would never be called if mayNeedRelaxation returned false, and I also wasn't sure what would happen if we never updated Inst. I can certainly try that approach, I agree the current check is not actually very good.

Copy link
Collaborator

@topperc topperc Mar 19, 2025

Choose a reason for hiding this comment

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

I'm not sure getRelaxedOpcode should be a member. It only has 2 callers, it's not virtual, and it doesn't use any members variables. It could be a file local static function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I will do that refactoring.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Warning(Parser.getTok().getLoc(),
"unknown option, expected 'push', 'pop', "
"'rvc', 'norvc', 'arch', 'relax', 'norelax', "
"'exact' or 'noexact'");
Copy link
Collaborator

Choose a reason for hiding this comment

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

while you're here. oxford comma

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

Learnt, the community is a good place to learn new English phrases. :-)

@@ -8,6 +8,7 @@

#include "RISCVAsmBackend.h"
#include "RISCVMCExpr.h"
#include "RISCVMCTargetDesc.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't think this is needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

.option noexact", "=^cr,^cr"(ptr %f)
ret i32 %0
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Two new lines at the end of the file.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

@svs-quic svs-quic left a comment

Choose a reason for hiding this comment

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

LGTM.

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -0,0 +1,20 @@
; RUN: llc -mtriple=riscv32 -mattr=+relax,+c %s --filetype=obj -o - \
Copy link
Member

Choose a reason for hiding this comment

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

Move to test/CodeGen/RISCV?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, yeah, this location was a little iffy, happier to have an llc test in the CodeGen directory, though this isn't the only one in the MC/RISCV directory

# RUN: | llvm-objdump --triple=riscv32 --mattr=+c --no-print-imm-hex -d - \
# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s
# RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+c %s \
# RUN: | llvm-objdump --triple=riscv32 --mattr=+c --no-print-imm-hex -d -M no-aliases - \
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need so many RUN lines? I think with no-aliases, we do not tests without -M no-aliases (the instruction aliases are the primary concerns of other tests, not this test)

Could this and optin-exact-relaxation.s be combined?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. Combined.

@lenary lenary merged commit 6a371c7 into llvm:main Mar 26, 2025
12 checks passed
@lenary lenary deleted the pr/riscv-noautocompress branch March 26, 2025 18:14
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 13, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122>.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
without single instruction encoding are not fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively).

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 14, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122>.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
without single instruction encoding are not fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively).

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 14, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
without single instruction encoding are not fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively).

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 14, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
without single instruction encoding are not fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively).

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 14, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
without single instruction encoding are not fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 14, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
without single instruction encoding are not fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 16, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
are not guaranteed to be fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 16, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

The main purpose of this mode is to emit desired machine code as the
user writes, assuming the user knows constraints of their code.  So,
macros like "li" (known to be expanded into possibly complex sequences)
are not guaranteed to be fully aware of this mode.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 17, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

Macros like "li" (known to be expanded into possibly complex sequences)
may still expand to complex instruction sequences but at least each
instruction emitted by macros is still subject to the behavior above.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/li32.s: Enable exact mode by external option.
	* testsuite/gas/riscv/li64.s: Likewise.
	* testsuite/gas/riscv/exact-li32.d: li32.d but enable exact mode
    to make sure that no automatic instruction compression occurs.
	* testsuite/gas/riscv/exact-li64.d: Likewise.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 26, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

Macros like "li" (known to be expanded into possibly complex sequences)
may still expand to complex instruction sequences but at least each
instruction emitted by macros is still subject to the behavior above.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/li32.s: Enable exact mode by external option.
	* testsuite/gas/riscv/li64.s: Likewise.
	* testsuite/gas/riscv/exact-li32.d: li32.d but enable exact mode
    to make sure that no automatic instruction compression occurs.
	* testsuite/gas/riscv/exact-li64.d: Likewise.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 26, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

Macros like "li" (known to be expanded into possibly complex sequences)
may still expand to complex instruction sequences but at least each
instruction emitted by macros is still subject to the behavior above.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/li32.s: Enable exact mode by external option.
	* testsuite/gas/riscv/li64.s: Likewise.
	* testsuite/gas/riscv/exact-li32.d: li32.d but enable exact mode
    to make sure that no automatic instruction compression occurs.
	* testsuite/gas/riscv/exact-li64.d: Likewise.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
a4lg added a commit to a4lg/binutils-gdb that referenced this pull request May 29, 2025
This commit adds two assembler directives: ".option exact" and
".option noexact" (enable/disable the exact mode) as discussed in
<riscv-non-isa/riscv-asm-manual#122> and
already implemented in LLVM.

When the exact mode is enabled,

1.  Linker relaxations are turned off,
2.  Instruction aliases that will change the encoding from the
    (likely non-alias) instruction with the same name are disabled
    (e.g. "addi" will never turn into "c.addi" even if optimizable) and
3.  Assembler relaxation of branch instructions are disabled
    (e.g. "blt" with a long offset will not turn into "bge + j").

Macros like "li" (known to be expanded into possibly complex sequences)
may still expand to complex instruction sequences but at least each
instruction emitted by macros is still subject to the behavior above.

Currently, interactions between ".option relax/norelax" and
".option exact/noexact" are designed to be LLVM-compatible (i.e.
".option exact/noexact" imply ".option norelax/relax", respectively)
but considered flaky and strongly discouraged from using both.

cf. <llvm/llvm-project#122483>

gas/ChangeLog:

	* config/tc-riscv.c (struct riscv_set_options): Add exact option.
	(RELAX_BRANCH_ENCODE): Encode exact option.
	(RELAX_BRANCH_EXACT): New predicate macro.
	(relaxed_branch_length): Handle exact mode cases.
	(append_insn): Pass exact option to RELAX_BRANCH_ENCODE.
	(riscv_ip): Skip instructions that would change the encoding
	when the exact mode is enabled.
	(s_riscv_option): Parse ".option exact" and ".option noexact"
	assembler directives.
	* doc/c-riscv.texi: Document new assembler directives.
	* testsuite/gas/riscv/exact.s: Test exact mode basics.
	* testsuite/gas/riscv/exact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local.s: Test conditional
	branches and unconditional jumps relative to a local symbol.
	* testsuite/gas/riscv/exact-branch-local-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-ok.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.d: Ditto.
	* testsuite/gas/riscv/exact-branch-local-exact-fail.l: Ditto.
	* testsuite/gas/riscv/exact-branch-extern.s: Test conditional
	branches and unconditional jumps relative to an external symbol.
	* testsuite/gas/riscv/exact-branch-extern-noexact.d: Ditto.
	* testsuite/gas/riscv/exact-branch-extern-exact.d: Ditto.
	* testsuite/gas/riscv/li32.s: Enable exact mode by external option.
	* testsuite/gas/riscv/li64.s: Likewise.
	* testsuite/gas/riscv/exact-li32.d: li32.d but enable exact mode
    to make sure that no automatic instruction compression occurs.
	* testsuite/gas/riscv/exact-li64.d: Likewise.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.s: Use exact
	mode to test various configurations and instructions.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.d: Ditto.
	* testsuite/gas/riscv/no-relax-branch-offset-fail.l: Ditto.

include/ChangeLog:

	* opcode/riscv.h (INSN_NON_EXACT): New flag to represent aliases
	to reject on the exact mode.

opcodes/ChangeLog:

	* riscv-opc.c (riscv_opcodes): Add INSN_NON_EXACT flag to all
	instructions that should be rejected on the exact mode.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:RISC-V mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants