IR: Introduce "float-abi" module flag (#210821)

This is intended to eliminate the FloatABIType TargetOptions field.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply@anthropic.com>
This commit is contained in:
Matt Arsenault 2026-07-30 10:49:48 +02:00 committed by GitHub
parent ef7f2f6119
commit 9aee37146d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 201 additions and 20 deletions

View file

@ -9407,6 +9407,39 @@ 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. For
instance, both ARM's soft and softfp modes would use this value, since
they share the same calling convention.
* - `"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

View file

@ -1049,6 +1049,15 @@ public:
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
/// @{

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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"));

View file

@ -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

View file

@ -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"}

View file

@ -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"}

View file

@ -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"}

View file

@ -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