https://github.com/arichardson updated 
https://github.com/llvm/llvm-project/pull/225129

>From e6963e9577a0a5e2b86c6da302a953d691c2ae6d Mon Sep 17 00:00:00 2001
From: Alex Richardson <[email protected]>
Date: Mon, 21 Sep 2026 08:58:45 -0700
Subject: [PATCH 1/4] [RISC-V][LTO] Add baseline tests for LTO inline assembly
 and mapping symbols

No functional change intended here, just adding test coverage for RISC-V LTO
inline assembly ABI handling (following up on
https://github.com/llvm/llvm-project/pull/223606) and for the `$x<arch>` ELF
mapping symbols emitted for module and function target features.

The `TODO`s for `.lto_discard` dropping module inline asm target features and
for the missing/duplicate `$x<arch>` mapping symbols will be addressed in the
following commits.

This commit was created with the help of AI tools
---
 clang/test/CodeGen/RISCV/lto-module-asm-abi.c | 36 +++++++
 cross-project-tests/riscv/lit.local.cfg       |  6 ++
 .../riscv/lto-inline-asm-abi.c                | 79 +++++++++++++++
 lld/test/ELF/lto/riscv-target-abi.ll          | 98 ++++++++++++++++---
 .../test/CodeGen/RISCV/module-asm-features.ll | 22 ++++-
 .../RISCV/riscv-func-target-feature.ll        | 60 +++++++++---
 llvm/test/LTO/RISCV/module-asm.ll             | 15 ++-
 llvm/test/MC/RISCV/mapping-across-sections.s  | 27 +++--
 8 files changed, 302 insertions(+), 41 deletions(-)
 create mode 100644 clang/test/CodeGen/RISCV/lto-module-asm-abi.c
 create mode 100644 cross-project-tests/riscv/lit.local.cfg
 create mode 100644 cross-project-tests/riscv/lto-inline-asm-abi.c

diff --git a/clang/test/CodeGen/RISCV/lto-module-asm-abi.c 
b/clang/test/CodeGen/RISCV/lto-module-asm-abi.c
new file mode 100644
index 0000000000000..cceafaac3ba0e
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/lto-module-asm-abi.c
@@ -0,0 +1,36 @@
+// REQUIRES: riscv-registered-target
+
+/// Regression test for https://github.com/llvm/llvm-project/pull/213410:
+/// Check that -march=rv64gcv -flto records +d in module asm and function
+/// target-features even though the driver only passes mcpu=generic-rv64 to 
lld.
+
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -flto %s -S 
-emit-llvm -o - \
+// RUN:   | FileCheck %s --check-prefix=IR
+
+// IR:      module asm(target_features: "{{.*}}+d{{.*}}", target_cpu: 
"generic-rv64")
+// IR-NEXT: "nop"
+// IR:      define dso_local void @_start() #[[#ATTR:]] {{.*}} {
+// IR-NEXT: entry:
+// IR-NEXT:   call void asm sideeffect "nop", ""()
+// IR-NEXT:   ret void
+// IR-NEXT: }
+// IR-EMPTY:
+// IR-NEXT: attributes #[[#ATTR]] = { {{.*}}"target-cpu"="generic-rv64" 
"target-features"="{{.*}}+d{{.*}}"
+// IR:      ![[#]] = !{i32 1, !"target-abi", !"lp64d"}
+// IR-NEXT: ![[#]] = !{i32 6, !"riscv-isa", ![[#ISA:]]}
+// IR-NEXT: ![[#ISA]] = !{!"{{.*}}_d2p2_{{.*}}"}
+
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -flto -shared 
-nostdlib -fuse-ld=lld %s -### 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=DRIVER
+
+// DRIVER: "-cc1"{{.*}}"-target-cpu" "generic-rv64"{{.*}}"-target-feature" 
"+d"{{.*}}"-target-abi" "lp64d"
+// DRIVER: "{{[^"]*}}ld.lld{{(\.exe)?}}"
+// DRIVER-NOT: mattr
+// DRIVER-SAME: "-plugin-opt=mcpu=generic-rv64"
+// DRIVER-NOT: mattr
+
+__asm__("nop");
+
+void _start(void) {
+  __asm__ volatile("nop");
+}
diff --git a/cross-project-tests/riscv/lit.local.cfg 
b/cross-project-tests/riscv/lit.local.cfg
new file mode 100644
index 0000000000000..e966a6d4ed56e
--- /dev/null
+++ b/cross-project-tests/riscv/lit.local.cfg
@@ -0,0 +1,6 @@
+if (
+    "clang" not in config.available_features
+    or "ld.lld" not in config.available_features
+    or "RISCV" not in config.targets_to_build
+):
+    config.unsupported = True
diff --git a/cross-project-tests/riscv/lto-inline-asm-abi.c 
b/cross-project-tests/riscv/lto-inline-asm-abi.c
new file mode 100644
index 0000000000000..3092708562686
--- /dev/null
+++ b/cross-project-tests/riscv/lto-inline-asm-abi.c
@@ -0,0 +1,79 @@
+// REQUIRES: ld.lld
+/// Regression test for https://github.com/llvm/llvm-project/pull/213410:
+/// Check that module-level inline assembly (including .symver imported by
+/// ThinLTO) and function-level inline assembly link cleanly under RegularLTO
+/// and ThinLTO when targeting riscv64 with lp64d ABI and -march=rv64gcv.
+// RUN: rm -rf %t && split-file %s %t
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -c 
%t/a.c -o %t1.o
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -c 
%t/b.c -o %t2.o
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -shared 
-nostdlib -fuse-ld=lld -Wl,--version-script=%t/ver.ver %t1.o %t2.o -o %t.so 
2>&1 \
+// RUN:   | FileCheck %s --allow-empty --implicit-check-not="error:" 
--implicit-check-not="warning:" --implicit-check-not="note:"
+// RUN: llvm-readobj --file-headers %t.so | FileCheck %s --check-prefix=FLAGS
+// RUN: llvm-objdump -d --show-all-symbols --no-show-raw-insn %t.so | 
FileCheck %s --check-prefix=DISASM
+// RUN: llvm-objdump -t %t.so | FileCheck %s --check-prefix=SYMS 
--implicit-check-not='\$x'
+//
+/// TODO: ThinLTO fails because IRMover drops TargetTriple when importing the
+/// module-level .symver inline asm into b.c's empty ThinLTO module, causing
+/// RISC-V module inline asm in a.c and b.c to use the default lp64 ABI instead
+/// of lp64d.
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto=thin -c 
%t/a.c -o %t1.thin.o
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto=thin -c 
%t/b.c -o %t2.thin.o
+// RUN: not %clang --target=riscv64-linux-android -march=rv64gcv -O2 
-flto=thin -shared -nostdlib -fuse-ld=lld -Wl,--version-script=%t/ver.ver 
%t1.thin.o %t2.thin.o -o %t.thin.so 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=THIN-ERR
+//
+// THIN-ERR: ld.lld: error: {{.*}}.lto.a.o: cannot link object files with 
different floating-point ABI
+//
+// FLAGS:      Flags [ (0x5)
+// FLAGS-NEXT:   EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
+// FLAGS-NEXT:   EF_RISCV_RVC (0x1)
+// FLAGS-NEXT: ]
+//
+/// TODO: RISCVTargetELFStreamer::emitTextAttribute does not update the
+/// streamer's ArchString when emitting the module's RISCVAttrs::ARCH attribute
+/// ("rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_...").
+// DISASM-LABEL: Disassembly of section .text:
+// DISASM-EMPTY:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]]:             nop
+// DISASM-EMPTY:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <symver_fn>:
+// DISASM-NEXT:  [[#%x,]]:             ret
+// DISASM-EMPTY:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <fn>:
+// DISASM-NEXT:  [[#%x,]]:             nop
+// DISASM-NEXT:  [[#%x,]]:             ret
+// DISASM-EMPTY:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <caller>:
+// DISASM-NEXT:  [[#%x,]]:             nop
+// DISASM-NEXT:  [[#%x,]]:             ret
+// DISASM-NOT:   {{.}}
+//
+// SYMS: [[#%x,]] l       .text        0000000000000000 $xrv64i2p1{{$}}
+// SYMS: [[#%x,]] l       .text        0000000000000000 $xrv64i2p1{{$}}
+// SYMS: [[#%x,]] l       .text        0000000000000000 $xrv64i2p1{{$}}
+// SYMS: [[#%x,]] l       .text        0000000000000000 $xrv64i2p1{{$}}
+// SYMS: [[#%x,]] g     F .text        0000000000000004 fn{{$}}
+// SYMS: [[#%x,]] g     F .text        0000000000000002 symver_fn{{$}}
+// SYMS: [[#%x,]] g     F .text        0000000000000004 caller{{$}}
+
+//--- ver.ver
+VER_1.0 {};
+
+//--- a.c
+__asm__("nop");
+__asm__(".symver symver_fn, symver_fn@VER_1.0");
+
+void symver_fn(void) {}
+
+void fn(void) {
+  __asm__ volatile("nop");
+}
+
+//--- b.c
+extern void fn(void);
+void caller(void) {
+  fn();
+}
diff --git a/lld/test/ELF/lto/riscv-target-abi.ll 
b/lld/test/ELF/lto/riscv-target-abi.ll
index 23f722e91a6fb..b4e805e3c2187 100644
--- a/lld/test/ELF/lto/riscv-target-abi.ll
+++ b/lld/test/ELF/lto/riscv-target-abi.ll
@@ -1,27 +1,30 @@
 ; REQUIRES: riscv
+; RUN: rm -rf %t && split-file %s %t
 
-;; The module flag asks for lp64d, but without -mcpu we default to no D 
extension,
-;; so we print a warning and ignore the module flag.
-; RUN: llvm-as %s -o %t.bc
-; RUN: ld.lld -shared %t.bc -o %t.so 2>&1 | FileCheck %s --check-prefix=WARN \
+;--- no-ext.ll
+;; The module flag asks for lp64d, and _start() has no target-features 
attribute.
+;; Without -mcpu we default to no D extension, so RISCVSubtarget prints a note
+;; and ignores the module flag.
+; RUN: llvm-as %t/no-ext.ll -o %t/no-ext.bc
+; RUN: ld.lld -shared %t/no-ext.bc -o %t/no-ext.so 2>&1 | FileCheck %s 
--check-prefix=WARN \
 ; RUN:   --implicit-check-not="ignoring target-abi" 
--implicit-check-not="error:" --implicit-check-not="warning:"
 ; WARN: note: hard-float 'd' ABI can't be used for a target that doesn't 
support the D instruction set extension (ignoring target-abi)
 
 ;; TODO: This is inconsistent: RISCVAsmPrinter::emitStartOfAsmFile sets e_flags
 ;; based on the raw module flag not the ABI actually used for codegen.
 ;; This means we are setting EF_RISCV_FLOAT_ABI_DOUBLE on a file built for 
soft float ABI
-; RUN: llvm-readobj --file-headers %t.so | FileCheck %s 
--check-prefix=FLAGS-ABI-IGNORED
+; RUN: llvm-readobj --file-headers %t/no-ext.so | FileCheck %s 
--check-prefix=FLAGS-ABI-IGNORED
 ; FLAGS-ABI-IGNORED: Flags [ (0x4)
 ; FLAGS-ABI-IGNORED-NEXT: EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
 ; FLAGS-ABI-IGNORED-NEXT: ]
 
-;; Passing -mcpu that has D makes the ABI valid again, so no warning.
-; RUN: ld.lld -mllvm -mcpu=sifive-u74 -shared %t.bc -o %t.so 2>&1 | FileCheck 
%s --check-prefix=NOWARN --allow-empty \
-; RUN:   --implicit-check-not="error:" --implicit-check-not="warning:"
-; RUN: llvm-readobj --file-headers %t.so | FileCheck %s 
--check-prefix=FLAGS-MCPU
-; RUN: ld.lld -plugin-opt=mcpu=sifive-u74 -shared %t.bc -o %t.so 2>&1 | 
FileCheck %s --check-prefix=NOWARN --allow-empty \
-; RUN:   --implicit-check-not="error:" --implicit-check-not="warning:"
-; RUN: llvm-readobj --file-headers %t.so | FileCheck %s 
--check-prefix=FLAGS-MCPU
+;; Passing -mcpu that has D makes the ABI valid again, so no warning/note.
+; RUN: ld.lld -mllvm -mcpu=sifive-u74 -shared %t/no-ext.bc -o %t/no-ext.so 
2>&1 | FileCheck %s --check-prefix=NOWARN --allow-empty \
+; RUN:   --implicit-check-not="error:" --implicit-check-not="warning:" 
--implicit-check-not="note:"
+; RUN: llvm-readobj --file-headers %t/no-ext.so | FileCheck %s 
--check-prefix=FLAGS-MCPU
+; RUN: ld.lld -plugin-opt=mcpu=sifive-u74 -shared %t/no-ext.bc -o %t/no-ext.so 
2>&1 | FileCheck %s --check-prefix=NOWARN --allow-empty \
+; RUN:   --implicit-check-not="error:" --implicit-check-not="warning:" 
--implicit-check-not="note:"
+; RUN: llvm-readobj --file-headers %t/no-ext.so | FileCheck %s 
--check-prefix=FLAGS-MCPU
 ; NOWARN-NOT: ignoring target-abi
 ; FLAGS-MCPU: Flags [ (0x5)
 ; FLAGS-MCPU-NEXT: EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
@@ -32,9 +35,7 @@ target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
 target triple = "riscv64"
 
 module asm "nop"
-;; Module asm with target features not including 'd' (would fail before fix)
 module asm(target_features: "+c") "c.nop"
-;; Module asm with target features enabling 'd'
 module asm(target_features: "+d") "fld f0, 0(sp)"
 
 define void @_start() {
@@ -44,3 +45,72 @@ define void @_start() {
 
 !llvm.module.flags = !{!0}
 !0 = !{i32 1, !"target-abi", !"lp64d"}
+
+;--- fn-inline-asm-no-ext.ll
+;; Function-level inline asm without +d on the function emits the missing D
+;; note once from RISCVSubtarget, without re-validating target-abi in 
RISCVAsmParser.
+; RUN: llvm-as %t/fn-inline-asm-no-ext.ll -o %t/fn-inline-asm-no-ext.bc
+; RUN: ld.lld -shared %t/fn-inline-asm-no-ext.bc -o %t/fn-inline-asm-no-ext.so 
2>&1 | FileCheck %s --check-prefix=WARN \
+; RUN:   --implicit-check-not="ignoring target-abi" 
--implicit-check-not="error:" --implicit-check-not="warning:"
+
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
+target triple = "riscv64"
+
+define void @_start() {
+  call void asm sideeffect "nop", ""()
+  ret void
+}
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"target-abi", !"lp64d"}
+
+;--- module-asm-no-ext.ll
+;; Module-level inline asm without target_features (e.g. Rust global_asm!) 
should
+;; not warn when functions in the module have +f,+d.
+; RUN: llvm-as %t/module-asm-no-ext.ll -o %t/module-asm-no-ext.bc
+; RUN: ld.lld -plugin-opt=mcpu=generic-rv64 -shared %t/module-asm-no-ext.bc -o 
%t/module-asm-no-ext.so 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=NOWARN --allow-empty \
+; RUN:       --implicit-check-not="ignoring target-abi" 
--implicit-check-not="error:" --implicit-check-not="warning:" 
--implicit-check-not="note:"
+; RUN: ld.lld -plugin-opt=mcpu=sifive-u74 -shared %t/module-asm-no-ext.bc -o 
%t/module-asm-no-ext.so 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=NOWARN --allow-empty \
+; RUN:       --implicit-check-not="error:" --implicit-check-not="warning:" 
--implicit-check-not="note:"
+
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
+target triple = "riscv64"
+
+module asm "nop"
+
+define void @_start() #0 {
+  ret void
+}
+attributes #0 = { "target-features"="+f,+d" }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"target-abi", !"lp64d"}
+
+;--- module-asm-abi.ll
+;; Regression test for https://github.com/llvm/llvm-project/pull/213410:
+;; Module asm and function target-features specifying +c,+d should link cleanly
+;; even when the LTO backend is invoked with -plugin-opt=mcpu=generic-rv64.
+; RUN: llvm-as %t/module-asm-abi.ll -o %t/module-asm-abi.bc
+; RUN: ld.lld -plugin-opt=mcpu=generic-rv64 -shared %t/module-asm-abi.bc -o 
%t/module-asm-abi.so 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=NOWARN --allow-empty \
+; RUN:       --implicit-check-not="error:" --implicit-check-not="warning:" 
--implicit-check-not="note:"
+; RUN: llvm-readobj --file-headers %t/module-asm-abi.so | FileCheck %s 
--check-prefix=FLAGS-MCPU
+
+target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
+target triple = "riscv64"
+
+module asm(target_features: "+c,+d")
+    "nop"
+
+define void @_start() #0 {
+  call void asm sideeffect "nop", ""()
+  ret void
+}
+attributes #0 = { "target-features"="+c,+d" }
+
+!llvm.module.flags = !{!0, !1}
+!0 = !{i32 1, !"target-abi", !"lp64d"}
+!1 = !{i32 6, !"riscv-isa", !2}
+!2 = !{!"rv64i2p1_c2p0_d2p2"}
diff --git a/llvm/test/CodeGen/RISCV/module-asm-features.ll 
b/llvm/test/CodeGen/RISCV/module-asm-features.ll
index ab16acab48688..63fbce7a4ea65 100644
--- a/llvm/test/CodeGen/RISCV/module-asm-features.ll
+++ b/llvm/test/CodeGen/RISCV/module-asm-features.ll
@@ -1,14 +1,28 @@
 ; RUN: llc -mtriple=riscv64-unknown-linux-gnu < %s | FileCheck %s 
--check-prefixes=CHECK,EXTRA-FEATURES
 ; RUN: llc -mtriple=riscv64-unknown-linux-gnu -mattr=+d < %s | FileCheck %s 
--check-prefixes=CHECK,SAME-FEATURES
+; RUN: llc -mtriple=riscv64-unknown-linux-gnu -filetype=obj < %s | 
llvm-objdump -d --show-all-symbols --no-show-raw-insn - | FileCheck %s 
--check-prefix=OBJ
 
 ; This should work fine, because the module asm specifies the necessary
 ; target features
 
 ; SAME-FEATURES-NOT: .option arch
-; EXTRA-FEATURES: .option push
-; EXTRA-FEATURES: .option arch, +d
-; CHECK: fld ft0, 0(sp)
-; EXTRA-FEATURES: .option pop
+; EXTRA-FEATURES:      .option push
+; EXTRA-FEATURES-NEXT: .option arch, +d, +f, +zicsr{{$}}
+; CHECK:        .globl func
+; CHECK-NEXT: func:
+; CHECK-NEXT:   fld ft0, 0(sp)
+; CHECK-NEXT:   ret
+; EXTRA-FEATURES-NEXT: .option pop
+
+;; TODO: emitTargetFeaturePush does not call setArchString(), so the mapping
+;; symbol does not record +d/+f/+zicsr when assembling directly to an object 
file.
+; OBJ-LABEL: Disassembly of section .text:
+; OBJ-EMPTY:
+; OBJ-NEXT:  0000000000000000 <$xrv64i2p1>:
+; OBJ-NEXT:  0000000000000000 <func>:
+; OBJ-NEXT:         0:         fld     ft0, 0x0(sp)
+; OBJ-NEXT:         4:         ret
+; OBJ-NOT:   {{.}}
 
 module asm(target_features: "+d")
     ".globl func"
diff --git a/llvm/test/CodeGen/RISCV/riscv-func-target-feature.ll 
b/llvm/test/CodeGen/RISCV/riscv-func-target-feature.ll
index d627ae9c90394..de3de8c27df1d 100644
--- a/llvm/test/CodeGen/RISCV/riscv-func-target-feature.ll
+++ b/llvm/test/CodeGen/RISCV/riscv-func-target-feature.ll
@@ -1,44 +1,72 @@
 ; RUN: llc -mtriple=riscv64 -mcpu=sifive-u74 -verify-machineinstrs < %s | 
FileCheck %s
+; RUN: llc -mtriple=riscv64 -mcpu=sifive-u74 -filetype=obj < %s \
+; RUN:   | llvm-objdump -d --show-all-symbols --no-show-raw-insn - | FileCheck 
%s --check-prefix=OBJ
 
-; CHECK: .option push
-; CHECK-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, 
+zvl128b, +zvl32b, +zvl64b
+;; TODO: emitTargetFeaturePush does not call setArchString(), so per-function
+;; target-features are not reflected in the $x<arch> mapping symbols.
+; OBJ-LABEL: Disassembly of section .text:
+; OBJ-EMPTY:
+; OBJ-NEXT:  0000000000000000 
<$xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0>:
+; OBJ-NEXT:  0000000000000000 <test1>:
+; OBJ-NEXT:         0:         ret
+; OBJ-EMPTY:
+; OBJ-NEXT:  0000000000000002 <test2>:
+; OBJ-NEXT:         2:         ret
+; OBJ-EMPTY:
+; OBJ-NEXT:  0000000000000004 <test3>:
+; OBJ-NEXT:         4:         ret
+; OBJ-EMPTY:
+; OBJ-NEXT:  0000000000000006 <test4>:
+; OBJ-NEXT:         6:         ret
+; OBJ-EMPTY:
+; OBJ-NEXT:  0000000000000008 <test5>:
+; OBJ-NEXT:         8:         ret
+; OBJ-NOT:   {{.}}
+
+; CHECK:      .option push
+; CHECK-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, 
+zvl128b, +zvl32b, +zvl64b{{$}}
 define void @test1() 
"target-features"="+a,+d,+f,+m,+c,+v,+zifencei,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl32b,+zvl64b"
 {
-; CHECK-LABEL: test1
-; CHECK: .option pop
+; CHECK-LABEL: test1:
+; CHECK:       ret
+; CHECK:       .option pop
 entry:
   ret void
 }
 
-; CHECK: .option push
-; CHECK-NEXT: .option arch, +zihintntl
+; CHECK-NEXT: .option push
+; CHECK-NEXT: .option arch, +zihintntl{{$}}
 define void @test2() "target-features"="+a,+d,+f,+m,+zihintntl,+zifencei" {
-; CHECK-LABEL: test2
-; CHECK: .option pop
+; CHECK-LABEL: test2:
+; CHECK:       ret
+; CHECK:       .option pop
 entry:
   ret void
 }
 
-; CHECK: .option push
-; CHECK-NEXT: .option arch, -a, -d, -f, -m
+; CHECK-NEXT: .option push
+; CHECK-NEXT: .option arch, -a, -d, -f, -m, -zcd{{$}}
 define void @test3() "target-features"="-a,-d,-f,-m" {
-; CHECK-LABEL: test3
-; CHECK: .option pop
+; CHECK-LABEL: test3:
+; CHECK:       ret
+; CHECK:       .option pop
 entry:
   ret void
 }
 
 ; CHECK-NOT: .option push
 define void @test4() {
-; CHECK-LABEL: test4
-; CHECK-NOT: .option pop
+; CHECK-LABEL: test4:
+; CHECK:       ret
+; CHECK-NOT:   .option pop
 entry:
   ret void
 }
 
 ; CHECK-NOT: .option push
 define void @test5() "target-features"="+unaligned-scalar-mem" {
-; CHECK-LABEL: test5
-; CHECK-NOT: .option pop
+; CHECK-LABEL: test5:
+; CHECK:       ret
+; CHECK-NOT:   .option pop
 entry:
   ret void
 }
diff --git a/llvm/test/LTO/RISCV/module-asm.ll 
b/llvm/test/LTO/RISCV/module-asm.ll
index 73320185e778a..e213ec14a2a58 100644
--- a/llvm/test/LTO/RISCV/module-asm.ll
+++ b/llvm/test/LTO/RISCV/module-asm.ll
@@ -2,11 +2,24 @@
 ; RUN: llvm-lto2 run -save-temps -filetype=asm -o %t.s %t.o -r=%t.o,func,p
 ; RUN: llvm-nm %t.o | FileCheck %s --check-prefix NM
 ; RUN: llvm-nm %t.s.0.5.precodegen.bc | FileCheck %s --check-prefix NM
+; RUN: llvm-dis %t.s.0.5.precodegen.bc -o - | FileCheck %s --check-prefix=IR
 ; RUN: FileCheck %s --input-file %t.s.0
 
 ; NM: T func
 
-; CHECK: fld ft0, 0(sp)
+;; TODO: LTO::addRegularLTO prepends ".lto_discard" without preserving the
+;; existing module inline asm's TargetCPU and TargetFeatures.
+; IR:      module asm
+; IR-NEXT:     ".lto_discard"
+; IR-NEXT: module asm(target_features: "+d")
+; IR-NEXT:     ".globl func"
+; IR-NEXT:     "func:"
+; IR-NEXT:     "fld f0, 0(sp)"
+; IR-NEXT:     "ret"
+
+; CHECK-LABEL: func:
+; CHECK-NEXT:    fld ft0, 0(sp)
+; CHECK-NEXT:    ret
 
 target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
 target triple = "riscv64-unknown-linux-gnu"
diff --git a/llvm/test/MC/RISCV/mapping-across-sections.s 
b/llvm/test/MC/RISCV/mapping-across-sections.s
index ecb8292dd6664..9a741e792a244 100644
--- a/llvm/test/MC/RISCV/mapping-across-sections.s
+++ b/llvm/test/MC/RISCV/mapping-across-sections.s
@@ -18,6 +18,13 @@
         .text
         nop
 
+# Pushing a data section and popping back to .text should also preserve .text's
+# mapping symbol state and not emit a redundant $x.
+        .pushsection .starts_data
+        .word 42
+        .popsection
+        nop
+
 # With all those constraints, we want:
 #   + .text to have $x<ISA> at 0 and no others
 #   + .wibble to have $x<ISA> at 0 (each code section records the active ISA
@@ -28,9 +35,17 @@
 # CHECK: [[#WIBBLE:]]] .wibble
 # CHECK: [[#STARTS_DATA:]]] .starts_data
 
-# CHECK:    Value  Size Type    Bind   Vis     Ndx              Name
-# CHECK-RV32: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#TEXT]]        
$xrv32i2p1{{$}}
-# CHECK-RV64: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#TEXT]]        
$xrv64i2p1{{$}}
-# CHECK-RV32: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#WIBBLE]]      
$xrv32i2p1{{$}}
-# CHECK-RV64: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#WIBBLE]]      
$xrv64i2p1{{$}}
-# CHECK: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#STARTS_DATA]] $d{{$}}
+## TODO: RISCVELFStreamer::changeSection saves mapping symbol state to
+## getPreviousSection() instead of getCurrentSection() on popSection(), causing
+## a duplicate $x mapping symbol at offset 8 in .text.
+# CHECK:           Symbol table '.symtab' contains 5 entries:
+# CHECK-NEXT:         Num:    Value  Size Type    Bind   Vis     Ndx Name
+# CHECK-NEXT:           0: {{0+}}       0 NOTYPE  LOCAL  DEFAULT   UND {{$}}
+# CHECK-RV32-NEXT:      1: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#TEXT]]     
   $xrv32i2p1{{$}}
+# CHECK-RV64-NEXT:      1: {{0+}}       0 NOTYPE  LOCAL  DEFAULT [[#TEXT]]     
   $xrv64i2p1{{$}}
+# CHECK-RV32-NEXT:      2: 00000000     0 NOTYPE  LOCAL  DEFAULT [[#WIBBLE]]   
   $xrv32i2p1{{$}}
+# CHECK-RV64-NEXT:      2: {{0+}}       0 NOTYPE  LOCAL  DEFAULT [[#WIBBLE]]   
   $xrv64i2p1{{$}}
+# CHECK-NEXT:           3: {{0+}}       0 NOTYPE  LOCAL  DEFAULT 
[[#STARTS_DATA]] $d{{$}}
+# CHECK-RV32-NEXT:      4: 00000008     0 NOTYPE  LOCAL  DEFAULT [[#TEXT]]     
   $xrv32i2p1{{$}}
+# CHECK-RV64-NEXT:      4: {{0+}}8      0 NOTYPE  LOCAL  DEFAULT [[#TEXT]]     
   $xrv64i2p1{{$}}
+# CHECK-NOT:       {{.}}

>From dd8e0a6c3586572456c6548a2265f26895e58e12 Mon Sep 17 00:00:00 2001
From: Alex Richardson <[email protected]>
Date: Mon, 21 Sep 2026 10:02:58 -0700
Subject: [PATCH 2/4] clang-format

---
 cross-project-tests/.clang-format              | 2 ++
 cross-project-tests/riscv/lto-inline-asm-abi.c | 8 ++------
 2 files changed, 4 insertions(+), 6 deletions(-)
 create mode 100644 cross-project-tests/.clang-format

diff --git a/cross-project-tests/.clang-format 
b/cross-project-tests/.clang-format
new file mode 100644
index 0000000000000..f5e3ec5b16d1f
--- /dev/null
+++ b/cross-project-tests/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: LLVM
+ReflowComments: false
diff --git a/cross-project-tests/riscv/lto-inline-asm-abi.c 
b/cross-project-tests/riscv/lto-inline-asm-abi.c
index 3092708562686..8caf1bda734c9 100644
--- a/cross-project-tests/riscv/lto-inline-asm-abi.c
+++ b/cross-project-tests/riscv/lto-inline-asm-abi.c
@@ -68,12 +68,8 @@ __asm__(".symver symver_fn, symver_fn@VER_1.0");
 
 void symver_fn(void) {}
 
-void fn(void) {
-  __asm__ volatile("nop");
-}
+void fn(void) { __asm__ volatile("nop"); }
 
 //--- b.c
 extern void fn(void);
-void caller(void) {
-  fn();
-}
+void caller(void) { fn(); }

>From f2433b40cb1abdc28c9832ca1d49e262b9c39310 Mon Sep 17 00:00:00 2001
From: Alex Richardson <[email protected]>
Date: Mon, 21 Sep 2026 23:42:18 -0700
Subject: [PATCH 3/4] reformat and fix tests after rebase

---
 .../riscv/lto-inline-asm-abi.c                | 32 +++++++++++++------
 .../test/CodeGen/RISCV/module-asm-features.ll |  5 +--
 2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/cross-project-tests/riscv/lto-inline-asm-abi.c 
b/cross-project-tests/riscv/lto-inline-asm-abi.c
index 8caf1bda734c9..e89e1e4f0687d 100644
--- a/cross-project-tests/riscv/lto-inline-asm-abi.c
+++ b/cross-project-tests/riscv/lto-inline-asm-abi.c
@@ -6,22 +6,32 @@
 // RUN: rm -rf %t && split-file %s %t
 // RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -c 
%t/a.c -o %t1.o
 // RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -c 
%t/b.c -o %t2.o
-// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -shared 
-nostdlib -fuse-ld=lld -Wl,--version-script=%t/ver.ver %t1.o %t2.o -o %t.so 
2>&1 \
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto -shared 
-nostdlib -fuse-ld=lld -Wl,-save-temps -Wl,--version-script=%t/ver.ver %t1.o 
%t2.o -o %t.so 2>&1 \
 // RUN:   | FileCheck %s --allow-empty --implicit-check-not="error:" 
--implicit-check-not="warning:" --implicit-check-not="note:"
+// RUN: llvm-dis %t.so.0.5.precodegen.bc -o - | FileCheck %s 
--check-prefix=REGULAR-IR
 // RUN: llvm-readobj --file-headers %t.so | FileCheck %s --check-prefix=FLAGS
 // RUN: llvm-objdump -d --show-all-symbols --no-show-raw-insn %t.so | 
FileCheck %s --check-prefix=DISASM
 // RUN: llvm-objdump -t %t.so | FileCheck %s --check-prefix=SYMS 
--implicit-check-not='\$x'
 //
-/// TODO: ThinLTO fails because IRMover drops TargetTriple when importing the
-/// module-level .symver inline asm into b.c's empty ThinLTO module, causing
-/// RISC-V module inline asm in a.c and b.c to use the default lp64 ABI instead
-/// of lp64d.
 // RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto=thin -c 
%t/a.c -o %t1.thin.o
 // RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto=thin -c 
%t/b.c -o %t2.thin.o
-// RUN: not %clang --target=riscv64-linux-android -march=rv64gcv -O2 
-flto=thin -shared -nostdlib -fuse-ld=lld -Wl,--version-script=%t/ver.ver 
%t1.thin.o %t2.thin.o -o %t.thin.so 2>&1 \
-// RUN:   | FileCheck %s --check-prefix=THIN-ERR
+// RUN: %clang --target=riscv64-linux-android -march=rv64gcv -O2 -flto=thin 
-shared -nostdlib -fuse-ld=lld -Wl,-save-temps -Wl,--version-script=%t/ver.ver 
%t1.thin.o %t2.thin.o -o %t.thin.so 2>&1 \
+// RUN:   | FileCheck %s --allow-empty --implicit-check-not="error:" 
--implicit-check-not="warning:" --implicit-check-not="note:"
+// RUN: llvm-dis %t2.thin.o.5.precodegen.bc -o - | FileCheck %s 
--check-prefix=THIN-IR
+// RUN: llvm-readobj --file-headers %t.thin.so | FileCheck %s 
--check-prefix=FLAGS
+// RUN: llvm-objdump -d --show-all-symbols --no-show-raw-insn %t.thin.so | 
FileCheck %s --check-prefix=DISASM
+// RUN: llvm-objdump -t %t.thin.so | FileCheck %s --check-prefix=SYMS 
--implicit-check-not='\$x'
+//
+/// TODO: LTO::addRegularLTO and IRLinker::run drop target_features and
+/// target_cpu when synthesizing .lto_discard and imported .symver directives.
+// REGULAR-IR:      module asm{{$}}
+// REGULAR-IR-NEXT:     ".lto_discard "
+// REGULAR-IR-NEXT: module asm(target_features: "+64bit,{{.*}}", target_cpu: 
"generic-rv64")
+// REGULAR-IR-NEXT:     "nop"
+// REGULAR-IR-NEXT:     ".symver symver_fn, symver_fn@VER_1.0"
 //
-// THIN-ERR: ld.lld: error: {{.*}}.lto.a.o: cannot link object files with 
different floating-point ABI
+// THIN-IR:      module asm{{$}}
+// THIN-IR-NEXT:     ".symver symver_fn, symver_fn@VER_1.0"
 //
 // FLAGS:      Flags [ (0x5)
 // FLAGS-NEXT:   EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
@@ -72,4 +82,8 @@ void fn(void) { __asm__ volatile("nop"); }
 
 //--- b.c
 extern void fn(void);
-void caller(void) { fn(); }
+extern void symver_fn(void);
+void caller(void) {
+  fn();
+  symver_fn();
+}
diff --git a/llvm/test/CodeGen/RISCV/module-asm-features.ll 
b/llvm/test/CodeGen/RISCV/module-asm-features.ll
index 63fbce7a4ea65..7fee7188e0ed8 100644
--- a/llvm/test/CodeGen/RISCV/module-asm-features.ll
+++ b/llvm/test/CodeGen/RISCV/module-asm-features.ll
@@ -15,12 +15,13 @@
 ; EXTRA-FEATURES-NEXT: .option pop
 
 ;; TODO: emitTargetFeaturePush does not call setArchString(), so the mapping
-;; symbol does not record +d/+f/+zicsr when assembling directly to an object 
file.
+;; symbol does not record +d/+f/+zicsr when assembling directly to an object
+;; file, causing llvm-objdump to fail to disassemble `fld`.
 ; OBJ-LABEL: Disassembly of section .text:
 ; OBJ-EMPTY:
 ; OBJ-NEXT:  0000000000000000 <$xrv64i2p1>:
 ; OBJ-NEXT:  0000000000000000 <func>:
-; OBJ-NEXT:         0:         fld     ft0, 0x0(sp)
+; OBJ-NEXT:         0:         <unknown>
 ; OBJ-NEXT:         4:         ret
 ; OBJ-NOT:   {{.}}
 

>From 4d5b06abab3af7752baa4492701aaca2ad9763d3 Mon Sep 17 00:00:00 2001
From: Alex Richardson <[email protected]>
Date: Tue, 22 Sep 2026 22:52:23 -0700
Subject: [PATCH 4/4] address feedback

---
 lld/test/ELF/lto/riscv-target-abi.ll | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/lld/test/ELF/lto/riscv-target-abi.ll 
b/lld/test/ELF/lto/riscv-target-abi.ll
index b4e805e3c2187..c33b39a9a5508 100644
--- a/lld/test/ELF/lto/riscv-target-abi.ll
+++ b/lld/test/ELF/lto/riscv-target-abi.ll
@@ -1,6 +1,18 @@
 ; REQUIRES: riscv
 ; RUN: rm -rf %t && split-file %s %t
 
+; WARN: note: hard-float 'd' ABI can't be used for a target that doesn't 
support the D instruction set extension (ignoring target-abi)
+; NOWARN-NOT: ignoring target-abi
+
+; FLAGS-ABI-IGNORED:      Flags [ (0x4)
+; FLAGS-ABI-IGNORED-NEXT:   EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
+; FLAGS-ABI-IGNORED-NEXT: ]
+
+; FLAGS-MCPU:      Flags [ (0x5)
+; FLAGS-MCPU-NEXT:   EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
+; FLAGS-MCPU-NEXT:   EF_RISCV_RVC (0x1)
+; FLAGS-MCPU-NEXT: ]
+
 ;--- no-ext.ll
 ;; The module flag asks for lp64d, and _start() has no target-features 
attribute.
 ;; Without -mcpu we default to no D extension, so RISCVSubtarget prints a note
@@ -8,15 +20,11 @@
 ; RUN: llvm-as %t/no-ext.ll -o %t/no-ext.bc
 ; RUN: ld.lld -shared %t/no-ext.bc -o %t/no-ext.so 2>&1 | FileCheck %s 
--check-prefix=WARN \
 ; RUN:   --implicit-check-not="ignoring target-abi" 
--implicit-check-not="error:" --implicit-check-not="warning:"
-; WARN: note: hard-float 'd' ABI can't be used for a target that doesn't 
support the D instruction set extension (ignoring target-abi)
 
 ;; TODO: This is inconsistent: RISCVAsmPrinter::emitStartOfAsmFile sets e_flags
 ;; based on the raw module flag not the ABI actually used for codegen.
 ;; This means we are setting EF_RISCV_FLOAT_ABI_DOUBLE on a file built for 
soft float ABI
 ; RUN: llvm-readobj --file-headers %t/no-ext.so | FileCheck %s 
--check-prefix=FLAGS-ABI-IGNORED
-; FLAGS-ABI-IGNORED: Flags [ (0x4)
-; FLAGS-ABI-IGNORED-NEXT: EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
-; FLAGS-ABI-IGNORED-NEXT: ]
 
 ;; Passing -mcpu that has D makes the ABI valid again, so no warning/note.
 ; RUN: ld.lld -mllvm -mcpu=sifive-u74 -shared %t/no-ext.bc -o %t/no-ext.so 
2>&1 | FileCheck %s --check-prefix=NOWARN --allow-empty \
@@ -25,11 +33,6 @@
 ; RUN: ld.lld -plugin-opt=mcpu=sifive-u74 -shared %t/no-ext.bc -o %t/no-ext.so 
2>&1 | FileCheck %s --check-prefix=NOWARN --allow-empty \
 ; RUN:   --implicit-check-not="error:" --implicit-check-not="warning:" 
--implicit-check-not="note:"
 ; RUN: llvm-readobj --file-headers %t/no-ext.so | FileCheck %s 
--check-prefix=FLAGS-MCPU
-; NOWARN-NOT: ignoring target-abi
-; FLAGS-MCPU: Flags [ (0x5)
-; FLAGS-MCPU-NEXT: EF_RISCV_FLOAT_ABI_DOUBLE (0x4)
-; FLAGS-MCPU-NEXT: EF_RISCV_RVC (0x1)
-; FLAGS-MCPU-NEXT: ]
 
 target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
 target triple = "riscv64"
@@ -65,8 +68,8 @@ define void @_start() {
 !0 = !{i32 1, !"target-abi", !"lp64d"}
 
 ;--- module-asm-no-ext.ll
-;; Module-level inline asm without target_features (e.g. Rust global_asm!) 
should
-;; not warn when functions in the module have +f,+d.
+;; Module-level inline asm without target_features does not re-validate
+;; target-abi in RISCVAsmParser when functions in the module have +f,+d.
 ; RUN: llvm-as %t/module-asm-no-ext.ll -o %t/module-asm-no-ext.bc
 ; RUN: ld.lld -plugin-opt=mcpu=generic-rv64 -shared %t/module-asm-no-ext.bc -o 
%t/module-asm-no-ext.so 2>&1 \
 ; RUN:   | FileCheck %s --check-prefix=NOWARN --allow-empty \

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to