[llvm-branch-commits] [lldb] [lldb][RISCV] update RISCV target features in disassembler (PR #173047)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Feb 11 04:51:48 PST 2026
https://github.com/daniilavdeev updated https://github.com/llvm/llvm-project/pull/173047
>From 7fddcbedeb2bad6c9c52d72f8ac613785b91e8df Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Tue, 18 Nov 2025 05:37:24 +0000
Subject: [PATCH 1/3] [lldb][RISCV] update RISCV target features in
disassembler
This patch replaces the hardcoded RISCV feature flags in disassembler
with SubtargetFeatures provided by ArchSpec, which are derived from
the .riscv.attributes ELF section. This ensures the disassembler uses
exactly the RISCV extensions present in the executable, improving the
accuracy and reliability of the disassembly output.
---
.../Disassembler/LLVMC/DisassemblerLLVMC.cpp | 39 +++++++++++--------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp
index e8bb706f7aab6..04cd48f1f5dcb 100644
--- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp
+++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp
@@ -1590,23 +1590,28 @@ DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch,
}
if (triple.isRISCV() && !cpu_or_features_overriden) {
- uint32_t arch_flags = arch.GetFlags();
- if (arch_flags & ArchSpec::eRISCV_rvc)
- features_str += "+c,";
- if (arch_flags & ArchSpec::eRISCV_rve)
- features_str += "+e,";
- if ((arch_flags & ArchSpec::eRISCV_float_abi_single) ==
- ArchSpec::eRISCV_float_abi_single)
- features_str += "+f,";
- if ((arch_flags & ArchSpec::eRISCV_float_abi_double) ==
- ArchSpec::eRISCV_float_abi_double)
- features_str += "+f,+d,";
- if ((arch_flags & ArchSpec::eRISCV_float_abi_quad) ==
- ArchSpec::eRISCV_float_abi_quad)
- features_str += "+f,+d,+q,";
- // FIXME: how do we detect features such as `+a`, `+m`?
- // Turn them on by default now, since everyone seems to use them
- features_str += "+a,+m,";
+ auto subtarget_features = arch.GetSubtargetFeatures().getString();
+ if (!subtarget_features.empty()) {
+ features_str += subtarget_features;
+ } else {
+ uint32_t arch_flags = arch.GetFlags();
+ if (arch_flags & ArchSpec::eRISCV_rvc)
+ features_str += "+c,";
+ if (arch_flags & ArchSpec::eRISCV_rve)
+ features_str += "+e,";
+ if ((arch_flags & ArchSpec::eRISCV_float_abi_single) ==
+ ArchSpec::eRISCV_float_abi_single)
+ features_str += "+f,";
+ if ((arch_flags & ArchSpec::eRISCV_float_abi_double) ==
+ ArchSpec::eRISCV_float_abi_double)
+ features_str += "+f,+d,";
+ if ((arch_flags & ArchSpec::eRISCV_float_abi_quad) ==
+ ArchSpec::eRISCV_float_abi_quad)
+ features_str += "+f,+d,+q,";
+ // FIXME: how do we detect features such as `+a`, `+m`?
+ // Turn them on by default now, since everyone seems to use them
+ features_str += "+a,+m,";
+ }
}
// We use m_disasm_up.get() to tell whether we are valid or not, so if this
>From 891e170596beae3418c05a4e7f8446b51ad50b95 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Tue, 16 Dec 2025 17:06:29 +0000
Subject: [PATCH 2/3] [lldb][RISCV] add bitmanip disassembler test
---
lldb/test/API/riscv/disassembler/Makefile | 3 +
.../riscv/disassembler/TestDisassembler.py | 71 +++++++++++++++++++
lldb/test/API/riscv/disassembler/main.cpp | 12 ++++
3 files changed, 86 insertions(+)
create mode 100644 lldb/test/API/riscv/disassembler/Makefile
create mode 100644 lldb/test/API/riscv/disassembler/TestDisassembler.py
create mode 100644 lldb/test/API/riscv/disassembler/main.cpp
diff --git a/lldb/test/API/riscv/disassembler/Makefile b/lldb/test/API/riscv/disassembler/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/riscv/disassembler/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/riscv/disassembler/TestDisassembler.py b/lldb/test/API/riscv/disassembler/TestDisassembler.py
new file mode 100644
index 0000000000000..796136cedc4e1
--- /dev/null
+++ b/lldb/test/API/riscv/disassembler/TestDisassembler.py
@@ -0,0 +1,71 @@
+"""
+Tests that LLDB can correctly set up a disassembler using extensions from the .riscv.attributes section.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestDisassembler(TestBase):
+ expected_zbb_instrs = ["andn", "orn", "xnor", "rol", "ror", "ret"]
+
+ def _get_llvm_tool(self, tool):
+ clang = self.getCompiler()
+ bindir = os.path.dirname(clang)
+ candidate = os.path.join(bindir, tool)
+ if os.path.exists(candidate):
+ return candidate
+ return lldbutil.which(tool)
+
+ def _strip_riscv_attributes(self):
+ """
+ Strips the .riscv.attributes section.
+ """
+ exe = self.getBuildArtifact("a.out")
+ stripped = self.getBuildArtifact("stripped.out")
+
+ objcopy_path = self._get_llvm_tool("llvm-objcopy")
+ self.assertTrue(objcopy_path, "llvm-objcopy not found")
+
+ out = subprocess.run(
+ [objcopy_path, "--remove-section=.riscv.attributes", exe, stripped],
+ check=True,
+ )
+
+ return os.path.basename(stripped)
+
+ @skipIf(archs=no_match("^riscv.*"))
+ def test_without_riscv_attributes(self):
+ """
+ Tests disassembly of a riscv binary without the .riscv.attributes.
+ Without the .riscv.attributes section lldb won't set up a disassembler to
+ handle the bitmanip extension, so it is not expected to see zbb instructions
+ in the output.
+ """
+ self.build(dictionary={"CFLAGS_EXTRAS": "-march=rv64gc_zbb"})
+ stripped_exe = self._strip_riscv_attributes()
+
+ lldbutil.run_to_name_breakpoint(self, "main", exe_name=stripped_exe)
+
+ self.expect("disassemble --name do_zbb_stuff")
+ output = self.res.GetOutput()
+
+ for instr in self.expected_zbb_instrs:
+ self.assertFalse(instr in output, "Zbb instructions should not be disassembled")
+
+ @skipIf(archs=no_match("^riscv.*"))
+ def test_with_riscv_attributes(self):
+ """
+ Tests disassembly of a riscv binary with the .riscv.attributes.
+ """
+ self.build(dictionary={"CFLAGS_EXTRAS": "-march=rv64gc_zbb"})
+
+ lldbutil.run_to_name_breakpoint(self, "main")
+
+ self.expect("disassemble --name do_zbb_stuff")
+ output = self.res.GetOutput()
+
+ for instr in self.expected_zbb_instrs:
+ self.assertTrue(instr in output, "Invalid disassembler output")
diff --git a/lldb/test/API/riscv/disassembler/main.cpp b/lldb/test/API/riscv/disassembler/main.cpp
new file mode 100644
index 0000000000000..1d1f22d602e71
--- /dev/null
+++ b/lldb/test/API/riscv/disassembler/main.cpp
@@ -0,0 +1,12 @@
+void do_zbb_stuff() {
+ asm volatile("andn a2, a0, a1\n"
+ "orn a2, a0, a1\n"
+ "xnor a2, a0, a1\n"
+ "rol a2, a0, a1\n"
+ "ror a2, a0, a1\n");
+}
+
+int main() {
+ do_zbb_stuff();
+ return 0;
+}
>From 9928f73d39e2daf2b67f5331edd62025ef9296b7 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Wed, 11 Feb 2026 12:50:51 +0000
Subject: [PATCH 3/3] update tests
---
.../Makefile | 17 ++++++++
.../TestConflictingExtensions.py | 32 +++++++++++++++
.../file_with_zcd.c | 6 +++
.../file_with_zcmp.c | 6 +++
.../conflicting-extensions-disassembly/main.c | 8 ++++
lldb/test/API/riscv/disassembler/Makefile | 5 +++
.../riscv/disassembler/TestDisassembler.py | 40 ++++---------------
7 files changed, 82 insertions(+), 32 deletions(-)
create mode 100644 lldb/test/API/riscv/conflicting-extensions-disassembly/Makefile
create mode 100644 lldb/test/API/riscv/conflicting-extensions-disassembly/TestConflictingExtensions.py
create mode 100644 lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcd.c
create mode 100644 lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcmp.c
create mode 100644 lldb/test/API/riscv/conflicting-extensions-disassembly/main.c
diff --git a/lldb/test/API/riscv/conflicting-extensions-disassembly/Makefile b/lldb/test/API/riscv/conflicting-extensions-disassembly/Makefile
new file mode 100644
index 0000000000000..8218c4f01a508
--- /dev/null
+++ b/lldb/test/API/riscv/conflicting-extensions-disassembly/Makefile
@@ -0,0 +1,17 @@
+C_SOURCES := main.c file_with_zcd.c file_with_zcmp.c
+
+include Makefile.rules
+
+CFLAGS_NO_ARCH := $(filter-out -march=%,$(CFLAGS))
+
+file_with_zcd.o: file_with_zcd.c
+ $(CC) $(CFLAGS_NO_ARCH) -march=rv64gc -g -c $< -o $@
+
+file_with_zcmp.o: file_with_zcmp.c
+ $(CC) $(CFLAGS_NO_ARCH) -march=rv64imad_zcmp -g -c $< -o $@
+
+main.o: main.c
+ $(CC) $(CFLAGS_NO_ARCH) -march=rv64gc -g -c $< -o $@
+
+$(EXE): main.o file_with_zcd.o file_with_zcmp.o
+ $(CC) $^ $(LDFLAGS) -o $@
diff --git a/lldb/test/API/riscv/conflicting-extensions-disassembly/TestConflictingExtensions.py b/lldb/test/API/riscv/conflicting-extensions-disassembly/TestConflictingExtensions.py
new file mode 100644
index 0000000000000..217e6d56d91ad
--- /dev/null
+++ b/lldb/test/API/riscv/conflicting-extensions-disassembly/TestConflictingExtensions.py
@@ -0,0 +1,32 @@
+"""
+Tests that LLDB displays an appropriate warning when .riscv.attributes contains an invalid RISC-V arch string.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestRISCVConflictingExtensions(TestBase):
+ @skipIfLLVMTargetMissing("RISCV")
+ def test_conflicting_extensions(self):
+ """
+ This test demonstrates the scenario where:
+ 1. file_with_zcd.c is compiled with rv64gc (includes C and D).
+ 2. file_with_zcmp.c is compiled with rv64imad_zcmp (includes Zcmp).
+ 3. The linker merges .riscv.attributes, creating the union: C + D + Zcmp.
+
+ The Zcmp extension is incompatible with the C extension when the D extension is enabled.
+ Therefore, the arch string contains conflicting extensions, and LLDB should
+ display an appropriate warning in this case.
+ """
+ self.build()
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ output = self.res.GetOutput()
+
+ self.assertIn(
+ output,
+ "The .riscv.attributes section contains an invalid RISC-V arch string",
+ )
diff --git a/lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcd.c b/lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcd.c
new file mode 100644
index 0000000000000..389ba216f378b
--- /dev/null
+++ b/lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcd.c
@@ -0,0 +1,6 @@
+void function_with_zcd_instructions() {
+ asm volatile("c.fsdsp ft0, 0(sp)\n\t"
+ "c.fsdsp ft1, 8(sp)\n\t"
+ "c.fsdsp fa0, 16(sp)\n\t"
+ "c.fsdsp fa1, 24(sp)\n\t");
+}
diff --git a/lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcmp.c b/lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcmp.c
new file mode 100644
index 0000000000000..0dac9f75ebb7f
--- /dev/null
+++ b/lldb/test/API/riscv/conflicting-extensions-disassembly/file_with_zcmp.c
@@ -0,0 +1,6 @@
+void function_with_zcmp_extension() {
+ asm volatile("cm.push {ra, s0-s2}, -32\n\t"
+ "cm.mva01s s0, s1\n\t"
+ "cm.mvsa01 s0, s1\n\t"
+ "cm.popret {ra, s0-s2}, 32\n\t");
+}
diff --git a/lldb/test/API/riscv/conflicting-extensions-disassembly/main.c b/lldb/test/API/riscv/conflicting-extensions-disassembly/main.c
new file mode 100644
index 0000000000000..e2b40222999fe
--- /dev/null
+++ b/lldb/test/API/riscv/conflicting-extensions-disassembly/main.c
@@ -0,0 +1,8 @@
+extern void function_with_zcd_instructions();
+extern void function_with_zcmp_extension();
+
+int main() {
+ function_with_zcd_instructions();
+ function_with_zcmp_extension();
+ return 0;
+}
diff --git a/lldb/test/API/riscv/disassembler/Makefile b/lldb/test/API/riscv/disassembler/Makefile
index 99998b20bcb05..46072b5db2e72 100644
--- a/lldb/test/API/riscv/disassembler/Makefile
+++ b/lldb/test/API/riscv/disassembler/Makefile
@@ -1,3 +1,8 @@
CXX_SOURCES := main.cpp
+all: a.out stripped.out
+
include Makefile.rules
+
+stripped.out: a.out
+ $(OBJCOPY) --remove-section=.riscv.attributes a.out stripped.out
diff --git a/lldb/test/API/riscv/disassembler/TestDisassembler.py b/lldb/test/API/riscv/disassembler/TestDisassembler.py
index 796136cedc4e1..68ea5bfc1063d 100644
--- a/lldb/test/API/riscv/disassembler/TestDisassembler.py
+++ b/lldb/test/API/riscv/disassembler/TestDisassembler.py
@@ -9,34 +9,9 @@
class TestDisassembler(TestBase):
- expected_zbb_instrs = ["andn", "orn", "xnor", "rol", "ror", "ret"]
+ expected_zbb_instrs = ["andn", "orn", "xnor", "rol", "ror"]
- def _get_llvm_tool(self, tool):
- clang = self.getCompiler()
- bindir = os.path.dirname(clang)
- candidate = os.path.join(bindir, tool)
- if os.path.exists(candidate):
- return candidate
- return lldbutil.which(tool)
-
- def _strip_riscv_attributes(self):
- """
- Strips the .riscv.attributes section.
- """
- exe = self.getBuildArtifact("a.out")
- stripped = self.getBuildArtifact("stripped.out")
-
- objcopy_path = self._get_llvm_tool("llvm-objcopy")
- self.assertTrue(objcopy_path, "llvm-objcopy not found")
-
- out = subprocess.run(
- [objcopy_path, "--remove-section=.riscv.attributes", exe, stripped],
- check=True,
- )
-
- return os.path.basename(stripped)
-
- @skipIf(archs=no_match("^riscv.*"))
+ @skipIfLLVMTargetMissing("RISCV")
def test_without_riscv_attributes(self):
"""
Tests disassembly of a riscv binary without the .riscv.attributes.
@@ -45,24 +20,25 @@ def test_without_riscv_attributes(self):
in the output.
"""
self.build(dictionary={"CFLAGS_EXTRAS": "-march=rv64gc_zbb"})
- stripped_exe = self._strip_riscv_attributes()
- lldbutil.run_to_name_breakpoint(self, "main", exe_name=stripped_exe)
+ target = self.dbg.CreateTarget(self.getBuildArtifact("stripped.out"))
self.expect("disassemble --name do_zbb_stuff")
output = self.res.GetOutput()
for instr in self.expected_zbb_instrs:
- self.assertFalse(instr in output, "Zbb instructions should not be disassembled")
+ self.assertFalse(
+ instr in output, "Zbb instructions should not be disassembled"
+ )
- @skipIf(archs=no_match("^riscv.*"))
+ @skipIfLLVMTargetMissing("RISCV")
def test_with_riscv_attributes(self):
"""
Tests disassembly of a riscv binary with the .riscv.attributes.
"""
self.build(dictionary={"CFLAGS_EXTRAS": "-march=rv64gc_zbb"})
- lldbutil.run_to_name_breakpoint(self, "main")
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.expect("disassemble --name do_zbb_stuff")
output = self.res.GetOutput()
More information about the llvm-branch-commits
mailing list