[llvm] IR: Introduce "float-abi" module flag (PR #210821)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 29 07:34:46 PDT 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/210821

>From e8e62788ce457ae0838dad2e4e991f13c0731be0 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 20 Jul 2026 12:52:33 +0200
Subject: [PATCH] IR: Introduce "float-abi" module flag

This is intended to eliminate the FloatABIType TargetOptions field.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
 llvm/docs/LangRef.md                          | 31 ++++++++++++++
 llvm/include/llvm/IR/Module.h                 |  9 ++++
 llvm/include/llvm/Support/CodeGen.h           | 41 +++++++++++++++----
 llvm/lib/IR/AutoUpgrade.cpp                   | 28 +++++++------
 llvm/lib/IR/Module.cpp                        |  6 +++
 llvm/lib/IR/Verifier.cpp                      | 10 +++++
 llvm/test/Assembler/module-flags-float-abi.ll | 13 ++++++
 llvm/test/Bitcode/upgrade-ppc-float-abi.ll    | 16 ++++++++
 llvm/test/Linker/module-flags-float-abi.ll    | 28 +++++++++++++
 llvm/test/Verifier/module-flags-float-abi.ll  | 37 +++++++++++++++++
 10 files changed, 199 insertions(+), 20 deletions(-)
 create mode 100644 llvm/test/Assembler/module-flags-float-abi.ll
 create mode 100644 llvm/test/Linker/module-flags-float-abi.ll
 create mode 100644 llvm/test/Verifier/module-flags-float-abi.ll

diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index a1c852e52c848..8c6cf2b6236ce 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -9407,6 +9407,37 @@ enum is the smallest type which can represent all of its values:
 !1 = !{i32 1, !"short_enum", i32 0}
 ```
 
+### Float ABI Module Flags Metadata
+
+This module flag describes the floating-point ABI (the calling convention used
+to pass and return floating-point values) that the module was compiled for. The
+value is an `MDString` and must be one of:
+
+```{list-table}
+:header-rows: 1
+:widths: 30 70
+* - Value
+  - Meaning
+
+* - `"soft"`
+  - The software floating-point calling convention is used: floating-point
+    values are passed in general-purpose (integer) registers. Note this is
+    independent of whether floating-point hardware is used to perform
+    operations; see the `use-soft-float` function attribute for that.
+
+* - `"hard"`
+  - The hardware floating-point calling convention is used: floating-point
+    values are passed in floating-point registers.
+```
+
+When the flag is absent, the target's default floating-point ABI is used. The
+flag must use the `error` merge behavior, so that linking modules with
+conflicting floating-point ABIs is rejected. For example:
+```
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"hard"}
+```
+
 ### Long Double Type Module Flags Metadata
 
 Describe the floating-point format used by libm for `long double`. The
diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h
index cfc6b34ced5ce..16dad6ad446fb 100644
--- a/llvm/include/llvm/IR/Module.h
+++ b/llvm/include/llvm/IR/Module.h
@@ -1049,6 +1049,15 @@ class LLVM_ABI Module {
   void setCodeModel(CodeModel::Model CL);
   /// @}
 
+  /// @}
+  /// @name Utility function for querying the floating-point ABI
+  /// @{
+
+  /// Returns the floating-point ABI recorded by the "float-abi" module flag, or
+  /// FloatABI::Default when the flag is absent (meaning the target default).
+  FloatABI::ABIType getFloatABI() const;
+  /// @}
+
   /// @}
   /// @name Utility function for querying and setting the large data threshold
   /// @{
diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h
index d29b24101c723..5bc3993a02dff 100644
--- a/llvm/include/llvm/Support/CodeGen.h
+++ b/llvm/include/llvm/Support/CodeGen.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_SUPPORT_CODEGEN_H
 #define LLVM_SUPPORT_CODEGEN_H
 
+#include "llvm/ADT/StringRef.h"
 #include <cstdint>
 #include <optional>
 
@@ -62,14 +63,6 @@ namespace llvm {
          ///< PPA1 is used instead of an .eh_frame section.
   };
 
-  namespace FloatABI {
-  enum ABIType {
-    Default, // Target-specific (either soft or hard depending on triple, etc).
-    Soft,    // Soft float.
-    Hard     // Hard float.
-  };
-  }
-
   /// The floating-point format used for the target's "long double" type.
   enum class LongDoubleFormat {
     IEEEsingle,
@@ -79,6 +72,38 @@ namespace llvm {
     PPCDoubleDouble,
   };
 
+  namespace FloatABI {
+  enum ABIType {
+    Default, // Target-specific (either soft or hard depending on triple, etc).
+    Soft,    // Soft float.
+    Hard     // Hard float.
+  };
+
+  /// Parse the string spelling used by the "float-abi" IR module flag into an
+  /// ABIType.
+  inline std::optional<ABIType> parseABIType(StringRef S) {
+    if (S == "soft")
+      return Soft;
+    if (S == "hard")
+      return Hard;
+    return std::nullopt;
+  }
+
+  /// Returns the string spelling used by the "float-abi" IR module flag for a
+  /// Soft or Hard ABIType. Default has no spelling.
+  inline StringRef getABITypeName(ABIType ABI) {
+    switch (ABI) {
+    case Soft:
+      return "soft";
+    case Hard:
+      return "hard";
+    case Default:
+      break;
+    }
+    return "";
+  }
+  } // namespace FloatABI
+
   enum class EABI {
     Unknown,
     Default, // Default means not specified
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index df79a934bd290..297bdee30760c 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -6629,22 +6629,26 @@ bool llvm::UpgradeModuleFlags(Module &M) {
 
     // clang/PowerPC used to use "float-abi" to describe the long double format;
     // it has been renamed to "long-double-type", with its values changed to the
-    // corresponding IR floating-point type names. Map any unrecognized value
-    // (which was never valid) to the PowerPC default ppc_fp128 rather than
-    // erasing the flag.
+    // corresponding IR floating-point type names.
     if (M.getTargetTriple().isPPC() && ID->getString() == "float-abi") {
       StringRef Format;
       if (auto *S = dyn_cast_or_null<MDString>(Op->getOperand(2)))
         Format = S->getString();
-      StringRef NewType = StringSwitch<StringRef>(Format)
-                              .Case("ieeequad", "fp128")
-                              .Case("ieeedouble", "double")
-                              .Default("ppc_fp128");
-      Metadata *Ops[3] = {Op->getOperand(0),
-                          MDString::get(M.getContext(), "long-double-type"),
-                          MDString::get(M.getContext(), NewType)};
-      ModFlags->setOperand(I, MDNode::get(M.getContext(), Ops));
-      Changed = true;
+
+      // The "float-abi" key is now reserved for the target-independent
+      // soft/hard ABI flag, so leave a valid value alone. Map any other value
+      // (including unrecognized ones, which were never valid) to the default.
+      if (!FloatABI::parseABIType(Format)) {
+        StringRef NewType = StringSwitch<StringRef>(Format)
+                                .Case("ieeequad", "fp128")
+                                .Case("ieeedouble", "double")
+                                .Default("ppc_fp128");
+        Metadata *Ops[3] = {Op->getOperand(0),
+                            MDString::get(M.getContext(), "long-double-type"),
+                            MDString::get(M.getContext(), NewType)};
+        ModFlags->setOperand(I, MDNode::get(M.getContext(), Ops));
+        Changed = true;
+      }
     }
   }
 
diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp
index a4a32613e6312..3dcc9ec5dac3f 100644
--- a/llvm/lib/IR/Module.cpp
+++ b/llvm/lib/IR/Module.cpp
@@ -682,6 +682,12 @@ void Module::setCodeModel(CodeModel::Model CL) {
   addModuleFlag(ModFlagBehavior::Error, "Code Model", CL);
 }
 
+FloatABI::ABIType Module::getFloatABI() const {
+  if (auto *Val = dyn_cast_or_null<MDString>(getModuleFlag("float-abi")))
+    return FloatABI::parseABIType(Val->getString()).value_or(FloatABI::Default);
+  return FloatABI::Default;
+}
+
 std::optional<uint64_t> Module::getLargeDataThreshold() const {
   auto *Val =
       cast_or_null<ConstantAsMetadata>(getModuleFlag("Large Data Threshold"));
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 90ad2e5bc8f68..50e7bc5edefea 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2013,6 +2013,16 @@ Verifier::visitModuleFlag(const MDNode *Op,
             "invalid long-double-type metadata value", Op);
   }
 
+  if (ID->getString() == "float-abi") {
+    Check(MFB == Module::Error,
+          "float-abi module flag must use 'error' merge behavior", Op);
+    const MDString *Value = dyn_cast_or_null<MDString>(Op->getOperand(2));
+    Check(Value, "float-abi metadata requires a string argument");
+    if (Value)
+      Check(FloatABI::parseABIType(Value->getString()).has_value(),
+            "invalid float-abi metadata value", Op);
+  }
+
   if (ID->getString() == "Linker Options") {
     // If the llvm.linker.options named metadata exists, we assume that the
     // bitcode reader has upgraded the module flag. Otherwise the flag might
diff --git a/llvm/test/Assembler/module-flags-float-abi.ll b/llvm/test/Assembler/module-flags-float-abi.ll
new file mode 100644
index 0000000000000..7aff95e950105
--- /dev/null
+++ b/llvm/test/Assembler/module-flags-float-abi.ll
@@ -0,0 +1,13 @@
+; RUN: split-file %s %t
+; RUN: llvm-as < %t/soft.ll | llvm-dis | FileCheck %s --check-prefix=SOFT
+; RUN: llvm-as < %t/hard.ll | llvm-dis | FileCheck %s --check-prefix=HARD
+
+;--- soft.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"soft"}
+; SOFT: !0 = !{i32 1, !"float-abi", !"soft"}
+
+;--- hard.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"hard"}
+; HARD: !0 = !{i32 1, !"float-abi", !"hard"}
diff --git a/llvm/test/Bitcode/upgrade-ppc-float-abi.ll b/llvm/test/Bitcode/upgrade-ppc-float-abi.ll
index 31df56d973723..592ee6990453a 100644
--- a/llvm/test/Bitcode/upgrade-ppc-float-abi.ll
+++ b/llvm/test/Bitcode/upgrade-ppc-float-abi.ll
@@ -8,6 +8,8 @@
 ; RUN: llvm-as < %t/ieeedouble.ll | llvm-dis | FileCheck %s --check-prefix=IEEEDOUBLE
 ; RUN: llvm-as < %t/unrecognized.ll | llvm-dis | FileCheck %s --check-prefix=UNRECOGNIZED
 ; RUN: llvm-as < %t/arm.ll | llvm-dis | FileCheck %s --check-prefix=ARM
+; RUN: llvm-as < %t/ppc-hard.ll | llvm-dis | FileCheck %s --check-prefix=HARD
+; RUN: llvm-as < %t/ppc-soft.ll | llvm-dis | FileCheck %s --check-prefix=SOFT
 
 ;; All the old PowerPC long double format spellings are upgraded to the new key
 ;; and IR type-name values.
@@ -43,3 +45,17 @@ target triple = "armv7-unknown-linux-gnueabihf"
 !llvm.module.flags = !{!0}
 !0 = !{i32 1, !"float-abi", !"hard"}
 ; ARM: !0 = !{i32 1, !"float-abi", !"hard"}
+
+;; The "float-abi" key now also names the target-independent soft/hard ABI flag;
+;; those values are valid and must be left untouched, even on PowerPC.
+;--- ppc-hard.ll
+target triple = "powerpc64le-unknown-linux-gnu"
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"hard"}
+; HARD: !0 = !{i32 1, !"float-abi", !"hard"}
+
+;--- ppc-soft.ll
+target triple = "powerpc64le-unknown-linux-gnu"
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"soft"}
+; SOFT: !0 = !{i32 1, !"float-abi", !"soft"}
diff --git a/llvm/test/Linker/module-flags-float-abi.ll b/llvm/test/Linker/module-flags-float-abi.ll
new file mode 100644
index 0000000000000..a53175373a985
--- /dev/null
+++ b/llvm/test/Linker/module-flags-float-abi.ll
@@ -0,0 +1,28 @@
+; RUN: split-file %s %t
+
+;; A module without the flag (target default) links cleanly with one that sets
+;; it, and the explicit value is preserved.
+; RUN: llvm-link %t/none.ll %t/hard.ll -S -o - | FileCheck %s --check-prefix=HARD
+; RUN: llvm-link %t/hard.ll %t/none.ll -S -o - | FileCheck %s --check-prefix=HARD
+
+;; Two modules that agree link cleanly.
+; RUN: llvm-link %t/hard.ll %t/hard.ll -S -o - | FileCheck %s --check-prefix=HARD
+
+;; Two modules that disagree are rejected by the 'error' merge behavior.
+; RUN: not llvm-link %t/hard.ll %t/soft.ll -S -o /dev/null 2>&1 | FileCheck %s --check-prefix=CONFLICT
+
+; HARD: !{i32 1, !"float-abi", !"hard"}
+; CONFLICT: linking module flags 'float-abi': IDs have conflicting values
+
+;--- none.ll
+define void @f() {
+  ret void
+}
+
+;--- hard.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"hard"}
+
+;--- soft.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"soft"}
diff --git a/llvm/test/Verifier/module-flags-float-abi.ll b/llvm/test/Verifier/module-flags-float-abi.ll
new file mode 100644
index 0000000000000..73cff95015052
--- /dev/null
+++ b/llvm/test/Verifier/module-flags-float-abi.ll
@@ -0,0 +1,37 @@
+; RUN: split-file %s %t
+; RUN: not llvm-as < %t/not-string.ll -disable-output 2>&1 | FileCheck %s --check-prefix=NOTSTRING
+; RUN: not llvm-as < %t/bad-value.ll -disable-output 2>&1 | FileCheck %s --check-prefix=BADVALUE
+; RUN: not llvm-as < %t/empty-value.ll -disable-output 2>&1 | FileCheck %s --check-prefix=EMPTY
+; RUN: not llvm-as < %t/too-few.ll -disable-output 2>&1 | FileCheck %s --check-prefix=TOOFEW
+; RUN: not llvm-as < %t/too-many.ll -disable-output 2>&1 | FileCheck %s --check-prefix=TOOMANY
+; RUN: not llvm-as < %t/bad-behavior.ll -disable-output 2>&1 | FileCheck %s --check-prefix=BEHAVIOR
+
+;--- not-string.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", i32 1}
+; NOTSTRING: float-abi metadata requires a string argument
+
+;--- bad-value.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"softfp"}
+; BADVALUE: invalid float-abi metadata value
+
+;--- empty-value.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !""}
+; EMPTY: invalid float-abi metadata value
+
+;--- too-few.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi"}
+; TOOFEW: incorrect number of operands in module flag
+
+;--- too-many.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"float-abi", !"hard", !"extra"}
+; TOOMANY: incorrect number of operands in module flag
+
+;--- bad-behavior.ll
+!llvm.module.flags = !{!0}
+!0 = !{i32 2, !"float-abi", !"hard"}
+; BEHAVIOR: float-abi module flag must use 'error' merge behavior



More information about the llvm-commits mailing list