https://github.com/Mintsuki created 
https://github.com/llvm/llvm-project/pull/223647

The LoongArch backend reads the ABI from the "target-abi" module flag, but
clang only emits that flag for ARM, PowerPC and RISC-V. Since LTO code
generation doesn't see the -target-abi option passed to clang, it falls back
to the default ABI of the target, which is lp64d for loongarch64-unknown-elf,
regardless of the ABI the code was compiled for.

When an FPU is enabled, this silently miscompiles code built for another ABI.
For example, with -mabi=lp64s, functions get the lp64d calling convention after
LTO, passing and returning floating-point values in FP registers instead of
general-purpose ones, and the output is marked as double-float. Nothing reports
this if every object goes through LTO. For instance, a shared library built this
way links without error, and so does lp64s code built without LTO that uses it.
Without an FPU, the generated code keeps the soft-float calling convention, but
the output is still marked as double-float.

The mismatch only shows up as an error when LTO output is linked together with
relocatable objects built without LTO, which LLD rejects with "cannot link
object files with different ABI".

This patch adds LoongArch to the targets for which clang emits the flag, so LTO
uses the ABI the code was compiled for. It follows the per-target approach taken
for ARM (#217601) and PowerPC (#221669), and leaves the existing TODO about
emitting the flag for every target with a non-empty ABI name as is.

With the flag present, LTO-linking bitcode built for different LoongArch ABIs
now fails with "linking module flags 'target-abi': IDs have conflicting values"
instead of silently picking a single ABI. A -target-abi given to the LTO backend
that disagrees with the flag is now also an error. Both match the existing
RISC-V behavior.

Assisted-by: Claude Code (Claude Opus 5)


>From 10ae7e607fd21fc11538bbb6b478974979745f33 Mon Sep 17 00:00:00 2001
From: Mintsuki <[email protected]>
Date: Tue, 15 Sep 2026 11:55:33 +0200
Subject: [PATCH] [clang][LoongArch] Emit "target-abi" module flag

The LoongArch backend takes the ABI from the "target-abi" module flag,
but clang only emitted that flag for ARM, PowerPC and RISC-V. LTO code
generation, which does not see the -target-abi option clang was invoked
with, thus fell back to the default ABI of the target, which is lp64d
for loongarch64-unknown-elf. Objects built with -mabi=lp64s and LTO then
ended up with the double-float ABI, which LLD refuses to link together
with the soft-float objects built without LTO.

Emit the flag for LoongArch as well.

Assisted-by: Claude Code (Claude Opus 5)
Signed-off-by: Mintsuki <[email protected]>
---
 clang/lib/CodeGen/CodeGenModule.cpp           |  9 ++++----
 .../LoongArch/target-abi-module-flag.c        | 21 +++++++++++++++++++
 2 files changed, 25 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGen/LoongArch/target-abi-module-flag.c

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 4839e2f45f72a..06b9ac9178f6f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1495,12 +1495,11 @@ void CodeGenModule::Release() {
   llvm::Triple T = Context.getTargetInfo().getTriple();
 
   // TODO: This should probably be just generally emitted for non-empty ABI
-  // names. LoongArch actively consumes the flag, but it is excluded here.
-  // Other targets have no apparent need for the ABI name, but set a non-empty
-  // value.
+  // names. Other targets have no apparent need for the ABI name, but set a
+  // non-empty value.
   if (StringRef ABIStr = Target.getABI();
-      !ABIStr.empty() &&
-      (T.isARM() || T.isThumb() || T.isRISCV() || T.isPPC())) {
+      !ABIStr.empty() && (T.isARM() || T.isThumb() || T.isRISCV() ||
+                          T.isPPC() || T.isLoongArch())) {
     getModule().addModuleFlag(llvm::Module::Error, "target-abi",
                               llvm::MDString::get(VMContext, ABIStr));
   }
diff --git a/clang/test/CodeGen/LoongArch/target-abi-module-flag.c 
b/clang/test/CodeGen/LoongArch/target-abi-module-flag.c
new file mode 100644
index 0000000000000..5a4826aff180b
--- /dev/null
+++ b/clang/test/CodeGen/LoongArch/target-abi-module-flag.c
@@ -0,0 +1,21 @@
+// Check that clang emits the "target-abi" module flag for LoongArch using the
+// target ABI string.
+
+// Default ABIs (no -target-abi override).
+// RUN: %clang_cc1 -triple loongarch32 -emit-llvm -o - %s | FileCheck 
--check-prefix=ILP32D %s
+// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -o - %s | FileCheck 
--check-prefix=LP64D %s
+
+// Explicit -target-abi overrides differing from the triple default.
+// RUN: %clang_cc1 -triple loongarch32 -target-abi ilp32f -emit-llvm -o - %s | 
FileCheck --check-prefix=ILP32F %s
+// RUN: %clang_cc1 -triple loongarch32 -target-abi ilp32s -emit-llvm -o - %s | 
FileCheck --check-prefix=ILP32S %s
+// RUN: %clang_cc1 -triple loongarch64 -target-abi lp64f -emit-llvm -o - %s | 
FileCheck --check-prefix=LP64F %s
+// RUN: %clang_cc1 -triple loongarch64 -target-abi lp64s -emit-llvm -o - %s | 
FileCheck --check-prefix=LP64S %s
+
+// ILP32D: !{i32 1, !"target-abi", !"ilp32d"}
+// ILP32F: !{i32 1, !"target-abi", !"ilp32f"}
+// ILP32S: !{i32 1, !"target-abi", !"ilp32s"}
+// LP64D: !{i32 1, !"target-abi", !"lp64d"}
+// LP64F: !{i32 1, !"target-abi", !"lp64f"}
+// LP64S: !{i32 1, !"target-abi", !"lp64s"}
+
+int x;

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

Reply via email to