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:
parent
ef7f2f6119
commit
9aee37146d
10 changed files with 201 additions and 20 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
/// @{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
13
llvm/test/Assembler/module-flags-float-abi.ll
Normal file
13
llvm/test/Assembler/module-flags-float-abi.ll
Normal 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"}
|
||||
|
|
@ -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"}
|
||||
|
|
|
|||
28
llvm/test/Linker/module-flags-float-abi.ll
Normal file
28
llvm/test/Linker/module-flags-float-abi.ll
Normal 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"}
|
||||
37
llvm/test/Verifier/module-flags-float-abi.ll
Normal file
37
llvm/test/Verifier/module-flags-float-abi.ll
Normal 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
|
||||
Loading…
Reference in a new issue