[llvm] [ABI] Add DefaultTargetInfo for default argument/return classification (PR #225009)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 00:01:47 PDT 2026
https://github.com/skc7 updated https://github.com/llvm/llvm-project/pull/225009
>From 9ee03c62ceb24f650755053ffe0d5b29b1485b5d Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Mon, 21 Sep 2026 11:45:48 +0530
Subject: [PATCH 1/4] [ABI] Add default argument/return classifiers to
TargetInfo
---
llvm/include/llvm/ABI/TargetInfo.h | 8 +
llvm/lib/ABI/TargetInfo.cpp | 39 ++++
llvm/unittests/ABI/CMakeLists.txt | 1 +
llvm/unittests/ABI/TargetInfoTest.cpp | 171 ++++++++++++++++++
.../gn/secondary/llvm/unittests/ABI/BUILD.gn | 1 +
5 files changed, 220 insertions(+)
create mode 100644 llvm/unittests/ABI/TargetInfoTest.cpp
diff --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h
index 6f7f29e70bb3d..c9fcadf44268f 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -92,6 +92,14 @@ class TargetInfo {
/// return Ty unchanged.
LLVM_ABI const Type *useFirstFieldIfTransparentUnion(const Type *Ty) const;
+ /// Default argument classification for targets without special rules.
+ /// Aggregates are passed indirectly and small integers are promoted.
+ LLVM_ABI ArgInfo classifyDefaultArgumentType(const Type *Ty) const;
+
+ /// Default return classification for targets without special rules.
+ /// Void is ignored and aggregates are returned indirectly.
+ LLVM_ABI ArgInfo classifyDefaultReturnType(const Type *RetTy) const;
+
/// Apply rules for classifying return types that are common to all targets.
LLVM_ABI bool maybeCommonClassifyReturnType(FunctionInfo &FI) const;
diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp
index 9ad8cb8f5829a..66314d855df24 100644
--- a/llvm/lib/ABI/TargetInfo.cpp
+++ b/llvm/lib/ABI/TargetInfo.cpp
@@ -71,6 +71,45 @@ const Type *TargetInfo::useFirstFieldIfTransparentUnion(const Type *Ty) const {
return Ty;
}
+ArgInfo TargetInfo::classifyDefaultArgumentType(const Type *Ty) const {
+ Ty = useFirstFieldIfTransparentUnion(Ty);
+
+ if (isAggregateTypeForABI(Ty)) {
+ // A record that must stay in memory keeps ByVal, everything else indirect.
+ if (RecordArgABI RAA = getRecordArgABI(Ty))
+ return getNaturalAlignIndirect(Ty, /*ByVal=*/RAA == RAA_DirectInMemory);
+ return getNaturalAlignIndirect(Ty);
+ }
+
+ if (const auto *IT = dyn_cast<IntegerType>(Ty)) {
+ // A _BitInt wider than 128 bits does not fit in registers.
+ if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
+ return getNaturalAlignIndirect(Ty);
+ if (isPromotableInteger(IT))
+ return ArgInfo::getExtend(Ty);
+ }
+
+ return ArgInfo::getDirect();
+}
+
+ArgInfo TargetInfo::classifyDefaultReturnType(const Type *RetTy) const {
+ if (RetTy->isVoid())
+ return ArgInfo::getIgnore();
+
+ // Return values never use ByVal, matching the library's return convention.
+ if (isAggregateTypeForABI(RetTy))
+ return getNaturalAlignIndirect(RetTy, /*ByVal=*/false);
+
+ if (const auto *IT = dyn_cast<IntegerType>(RetTy)) {
+ if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
+ return getNaturalAlignIndirect(RetTy, /*ByVal=*/false);
+ if (isPromotableInteger(IT))
+ return ArgInfo::getExtend(RetTy);
+ }
+
+ return ArgInfo::getDirect();
+}
+
bool TargetInfo::maybeCommonClassifyReturnType(FunctionInfo &FI) const {
const abi::Type *Ty = FI.getReturnType();
diff --git a/llvm/unittests/ABI/CMakeLists.txt b/llvm/unittests/ABI/CMakeLists.txt
index 4a1910ab8809b..07c1cdbb9d80e 100644
--- a/llvm/unittests/ABI/CMakeLists.txt
+++ b/llvm/unittests/ABI/CMakeLists.txt
@@ -8,6 +8,7 @@ add_llvm_unittest(ABITests
AArch64TargetInfoTest.cpp
IRTypeMapperTest.cpp
FunctionInfoTest.cpp
+ TargetInfoTest.cpp
X86TargetInfoTest.cpp
TypesTest.cpp
)
diff --git a/llvm/unittests/ABI/TargetInfoTest.cpp b/llvm/unittests/ABI/TargetInfoTest.cpp
new file mode 100644
index 0000000000000..1f53776417ea2
--- /dev/null
+++ b/llvm/unittests/ABI/TargetInfoTest.cpp
@@ -0,0 +1,171 @@
+//===- TargetInfoTest.cpp - shared ABI TargetInfo unit tests --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for target-independent helpers on the TargetInfo base class. These are
+// exercised through a minimal concrete target so any new shared default lands
+// here rather than in a per-target test file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ABI/TargetInfo.h"
+#include "llvm/ABI/FunctionInfo.h"
+#include "llvm/ABI/Types.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/Allocator.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+// RecordFlags' bitmask operators are declared in namespace llvm, so combining
+// two of them needs that namespace visible.
+using namespace llvm;
+
+using ABIType = llvm::abi::Type;
+using llvm::abi::ArgInfo;
+using llvm::abi::FieldInfo;
+using llvm::abi::FunctionInfo;
+using llvm::abi::RecordFlags;
+using llvm::abi::StructPacking;
+using llvm::abi::TargetInfo;
+using llvm::abi::TypeBuilder;
+
+// A minimal concrete target that re-exposes the shared, protected default
+// classifiers so they can be exercised directly, independent of any real
+// target.
+class TestTargetInfo : public TargetInfo {
+public:
+ explicit TestTargetInfo(TypeBuilder &Builder) : TargetInfo(Builder) {}
+ void computeInfo(FunctionInfo &) const override {}
+ const llvm::abi::ABICompatInfo &getABICompatInfo() const override {
+ return Compat;
+ }
+ using TargetInfo::classifyDefaultArgumentType;
+ using TargetInfo::classifyDefaultReturnType;
+
+private:
+ llvm::abi::ABICompatInfo Compat;
+};
+
+class TargetInfoTest : public ::testing::Test {
+protected:
+ llvm::BumpPtrAllocator Alloc;
+ TypeBuilder TB;
+ const ABIType *I16;
+ const ABIType *I32;
+ const ABIType *Void;
+ /// A _BitInt wider than 128 bits, which cannot be passed in registers.
+ const ABIType *WideBitInt;
+
+ TargetInfoTest()
+ : TB(Alloc), I16(TB.getIntegerType(16, llvm::Align(2), /*Signed=*/true)),
+ I32(TB.getIntegerType(32, llvm::Align(4), /*Signed=*/true)),
+ Void(TB.getVoidType()),
+ WideBitInt(TB.getIntegerType(129, llvm::Align(8), /*Signed=*/true,
+ /*IsBitInt=*/true)) {}
+
+ /// A record with a single int field, passable in registers.
+ const ABIType *recordInReg() {
+ return TB.getRecordType({FieldInfo(I32, 0)}, llvm::TypeSize::getFixed(32),
+ llvm::Align(4), /*UnadjustedAlign=*/llvm::Align(4),
+ StructPacking::Default, {}, {},
+ RecordFlags::CanPassInRegisters);
+ }
+
+ /// The same record marked as unable to pass in registers, e.g. a non-trivial
+ /// C++ type. This takes the RAA_Indirect path.
+ const ABIType *recordInMemory() {
+ return TB.getRecordType({FieldInfo(I32, 0)}, llvm::TypeSize::getFixed(32),
+ llvm::Align(4), /*UnadjustedAlign=*/llvm::Align(4),
+ StructPacking::Default, {}, {}, RecordFlags::None);
+ }
+
+ ArgInfo classifyArg(const ABIType *Ty) {
+ TestTargetInfo TI(TB);
+ return TI.classifyDefaultArgumentType(Ty);
+ }
+
+ ArgInfo classifyRet(const ABIType *Ty) {
+ TestTargetInfo TI(TB);
+ return TI.classifyDefaultReturnType(Ty);
+ }
+};
+
+// --- Argument classification -------------------------------------------------
+
+// A word-sized integer is passed directly.
+TEST_F(TargetInfoTest, DefaultArgIntIsDirect) {
+ ArgInfo Info = classifyArg(I32);
+ EXPECT_TRUE(Info.isDirect());
+}
+
+// A sub-word integer is promoted.
+TEST_F(TargetInfoTest, DefaultArgSmallIntIsExtended) {
+ EXPECT_TRUE(classifyArg(I16).isExtend());
+}
+
+// A record that fits in registers is passed indirectly by value.
+TEST_F(TargetInfoTest, DefaultArgRecordInRegIsIndirectByVal) {
+ ArgInfo Info = classifyArg(recordInReg());
+ ASSERT_TRUE(Info.isIndirect());
+ EXPECT_TRUE(Info.getIndirectByVal());
+}
+
+// A record that cannot pass in registers goes indirect without ByVal.
+TEST_F(TargetInfoTest, DefaultArgRecordInMemoryIsIndirectNoByVal) {
+ ArgInfo Info = classifyArg(recordInMemory());
+ ASSERT_TRUE(Info.isIndirect());
+ EXPECT_FALSE(Info.getIndirectByVal());
+}
+
+// A _BitInt wider than 128 bits is passed indirectly.
+TEST_F(TargetInfoTest, DefaultArgWideBitIntIsIndirect) {
+ EXPECT_TRUE(classifyArg(WideBitInt).isIndirect());
+}
+
+// A transparent union is classified as its first field, so a union of one int
+// is passed directly rather than as an aggregate.
+TEST_F(TargetInfoTest, DefaultArgTransparentUnionUsesFirstField) {
+ const ABIType *U = TB.getUnionType(
+ {FieldInfo(I32, 0)}, llvm::TypeSize::getFixed(32), llvm::Align(4),
+ /*UnadjustedAlign=*/llvm::Align(4), StructPacking::Default,
+ RecordFlags::IsTransparent | RecordFlags::CanPassInRegisters);
+ EXPECT_TRUE(classifyArg(U).isDirect());
+}
+
+// --- Return classification ---------------------------------------------------
+
+// Void returns are ignored.
+TEST_F(TargetInfoTest, DefaultReturnVoidIsIgnored) {
+ EXPECT_TRUE(classifyRet(Void).isIgnore());
+}
+
+// A word-sized integer is returned directly.
+TEST_F(TargetInfoTest, DefaultReturnIntIsDirect) {
+ EXPECT_TRUE(classifyRet(I32).isDirect());
+}
+
+// A sub-word integer is promoted on return.
+TEST_F(TargetInfoTest, DefaultReturnSmallIntIsExtended) {
+ EXPECT_TRUE(classifyRet(I16).isExtend());
+}
+
+// An aggregate is returned indirectly, and returns never use ByVal.
+TEST_F(TargetInfoTest, DefaultReturnRecordIsIndirectNoByVal) {
+ ArgInfo Info = classifyRet(recordInReg());
+ ASSERT_TRUE(Info.isIndirect());
+ EXPECT_FALSE(Info.getIndirectByVal());
+}
+
+// A _BitInt wider than 128 bits is returned indirectly.
+TEST_F(TargetInfoTest, DefaultReturnWideBitIntIsIndirect) {
+ ArgInfo Info = classifyRet(WideBitInt);
+ ASSERT_TRUE(Info.isIndirect());
+ EXPECT_FALSE(Info.getIndirectByVal());
+}
+
+} // namespace
diff --git a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn
index 4c34fdf7647bd..e424496f45150 100644
--- a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn
@@ -10,6 +10,7 @@ unittest("ABITests") {
"AArch64TargetInfoTest.cpp",
"IRTypeMapperTest.cpp",
"FunctionInfoTest.cpp",
+ "TargetInfoTest.cpp",
"X86TargetInfoTest.cpp",
"TypesTest.cpp",
]
>From 8ba83236b8cedff56173d2533d14720641bd23af Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Mon, 21 Sep 2026 11:53:38 +0530
Subject: [PATCH 2/4] update
---
llvm/lib/ABI/TargetInfo.cpp | 6 +++---
llvm/unittests/ABI/TargetInfoTest.cpp | 12 ++++--------
2 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp
index 66314d855df24..21fe7c6ed341a 100644
--- a/llvm/lib/ABI/TargetInfo.cpp
+++ b/llvm/lib/ABI/TargetInfo.cpp
@@ -96,13 +96,13 @@ ArgInfo TargetInfo::classifyDefaultReturnType(const Type *RetTy) const {
if (RetTy->isVoid())
return ArgInfo::getIgnore();
- // Return values never use ByVal, matching the library's return convention.
if (isAggregateTypeForABI(RetTy))
- return getNaturalAlignIndirect(RetTy, /*ByVal=*/false);
+ return getNaturalAlignIndirect(RetTy);
if (const auto *IT = dyn_cast<IntegerType>(RetTy)) {
+ // A _BitInt wider than 128 bits does not fit in registers.
if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
- return getNaturalAlignIndirect(RetTy, /*ByVal=*/false);
+ return getNaturalAlignIndirect(RetTy);
if (isPromotableInteger(IT))
return ArgInfo::getExtend(RetTy);
}
diff --git a/llvm/unittests/ABI/TargetInfoTest.cpp b/llvm/unittests/ABI/TargetInfoTest.cpp
index 1f53776417ea2..acf4842777677 100644
--- a/llvm/unittests/ABI/TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/TargetInfoTest.cpp
@@ -154,18 +154,14 @@ TEST_F(TargetInfoTest, DefaultReturnSmallIntIsExtended) {
EXPECT_TRUE(classifyRet(I16).isExtend());
}
-// An aggregate is returned indirectly, and returns never use ByVal.
-TEST_F(TargetInfoTest, DefaultReturnRecordIsIndirectNoByVal) {
- ArgInfo Info = classifyRet(recordInReg());
- ASSERT_TRUE(Info.isIndirect());
- EXPECT_FALSE(Info.getIndirectByVal());
+// An aggregate is returned indirectly.
+TEST_F(TargetInfoTest, DefaultReturnRecordIsIndirect) {
+ EXPECT_TRUE(classifyRet(recordInReg()).isIndirect());
}
// A _BitInt wider than 128 bits is returned indirectly.
TEST_F(TargetInfoTest, DefaultReturnWideBitIntIsIndirect) {
- ArgInfo Info = classifyRet(WideBitInt);
- ASSERT_TRUE(Info.isIndirect());
- EXPECT_FALSE(Info.getIndirectByVal());
+ EXPECT_TRUE(classifyRet(WideBitInt).isIndirect());
}
} // namespace
>From fddb2882b29cbe9c25711374020f94b602c598bb Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Tue, 22 Sep 2026 10:19:19 +0530
Subject: [PATCH 3/4] Create DefaultTargetInfo class
---
llvm/include/llvm/ABI/DefaultTargetInfo.h | 38 ++++++++++++++
llvm/include/llvm/ABI/TargetInfo.h | 8 ---
llvm/lib/ABI/CMakeLists.txt | 1 +
llvm/lib/ABI/DefaultTargetInfo.cpp | 60 +++++++++++++++++++++++
llvm/lib/ABI/TargetInfo.cpp | 39 ---------------
llvm/unittests/ABI/TargetInfoTest.cpp | 22 ++++-----
6 files changed, 109 insertions(+), 59 deletions(-)
create mode 100644 llvm/include/llvm/ABI/DefaultTargetInfo.h
create mode 100644 llvm/lib/ABI/DefaultTargetInfo.cpp
diff --git a/llvm/include/llvm/ABI/DefaultTargetInfo.h b/llvm/include/llvm/ABI/DefaultTargetInfo.h
new file mode 100644
index 0000000000000..1df4191aca43c
--- /dev/null
+++ b/llvm/include/llvm/ABI/DefaultTargetInfo.h
@@ -0,0 +1,38 @@
+//===----- DefaultTargetInfo.h - Default ABI classification ------- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Default ABI classification shared by targets without special rules. This is
+/// the llvm::abi analogue of clang::CodeGen::DefaultABIInfo: targets delegate
+/// to it for the cases they do not handle themselves.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ABI_DEFAULTTARGETINFO_H
+#define LLVM_ABI_DEFAULTTARGETINFO_H
+
+#include "llvm/ABI/TargetInfo.h"
+
+namespace llvm {
+namespace abi {
+
+/// Self-consistent classification that conforms to no particular ABI.
+class DefaultTargetInfo : public TargetInfo {
+public:
+ using TargetInfo::TargetInfo;
+
+ LLVM_ABI ArgInfo classifyArgumentType(const Type *Ty) const;
+ LLVM_ABI ArgInfo classifyReturnType(const Type *RetTy) const;
+
+ LLVM_ABI void computeInfo(FunctionInfo &FI) const override;
+};
+
+} // namespace abi
+} // namespace llvm
+
+#endif // LLVM_ABI_DEFAULTTARGETINFO_H
diff --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h
index c9fcadf44268f..6f7f29e70bb3d 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -92,14 +92,6 @@ class TargetInfo {
/// return Ty unchanged.
LLVM_ABI const Type *useFirstFieldIfTransparentUnion(const Type *Ty) const;
- /// Default argument classification for targets without special rules.
- /// Aggregates are passed indirectly and small integers are promoted.
- LLVM_ABI ArgInfo classifyDefaultArgumentType(const Type *Ty) const;
-
- /// Default return classification for targets without special rules.
- /// Void is ignored and aggregates are returned indirectly.
- LLVM_ABI ArgInfo classifyDefaultReturnType(const Type *RetTy) const;
-
/// Apply rules for classifying return types that are common to all targets.
LLVM_ABI bool maybeCommonClassifyReturnType(FunctionInfo &FI) const;
diff --git a/llvm/lib/ABI/CMakeLists.txt b/llvm/lib/ABI/CMakeLists.txt
index 39e725ca9fd3b..831ea0715f7c0 100644
--- a/llvm/lib/ABI/CMakeLists.txt
+++ b/llvm/lib/ABI/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMABI
Types.cpp
FunctionInfo.cpp
TargetInfo.cpp
+ DefaultTargetInfo.cpp
IRTypeMapper.cpp
Targets/AArch64.cpp
Targets/BPF.cpp
diff --git a/llvm/lib/ABI/DefaultTargetInfo.cpp b/llvm/lib/ABI/DefaultTargetInfo.cpp
new file mode 100644
index 0000000000000..01380c106e6a3
--- /dev/null
+++ b/llvm/lib/ABI/DefaultTargetInfo.cpp
@@ -0,0 +1,60 @@
+//===- DefaultTargetInfo.cpp - Default ABI classification -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ABI/DefaultTargetInfo.h"
+#include "llvm/Support/Casting.h"
+
+using namespace llvm::abi;
+using llvm::dyn_cast;
+
+ArgInfo DefaultTargetInfo::classifyArgumentType(const Type *Ty) const {
+ Ty = useFirstFieldIfTransparentUnion(Ty);
+
+ if (isAggregateTypeForABI(Ty)) {
+ // Records with non-trivial destructors/copy-constructors should not be
+ // passed by value.
+ if (RecordArgABI RAA = getRecordArgABI(Ty))
+ return getNaturalAlignIndirect(Ty, /*ByVal=*/RAA == RAA_DirectInMemory);
+ return getNaturalAlignIndirect(Ty);
+ }
+
+ if (const auto *IT = dyn_cast<IntegerType>(Ty)) {
+ // A _BitInt wider than 128 bits does not fit in registers.
+ if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
+ return getNaturalAlignIndirect(Ty);
+ if (isPromotableInteger(IT))
+ return ArgInfo::getExtend(Ty);
+ }
+
+ return ArgInfo::getDirect();
+}
+
+ArgInfo DefaultTargetInfo::classifyReturnType(const Type *RetTy) const {
+ if (RetTy->isVoid())
+ return ArgInfo::getIgnore();
+
+ if (isAggregateTypeForABI(RetTy))
+ return getNaturalAlignIndirect(RetTy);
+
+ if (const auto *IT = dyn_cast<IntegerType>(RetTy)) {
+ // A _BitInt wider than 128 bits does not fit in registers.
+ if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
+ return getNaturalAlignIndirect(RetTy);
+ if (isPromotableInteger(IT))
+ return ArgInfo::getExtend(RetTy);
+ }
+
+ return ArgInfo::getDirect();
+}
+
+void DefaultTargetInfo::computeInfo(FunctionInfo &FI) const {
+ if (!maybeCommonClassifyReturnType(FI))
+ FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
+ for (auto &I : FI.arguments())
+ I.Info = classifyArgumentType(I.ABIType);
+}
diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp
index 21fe7c6ed341a..9ad8cb8f5829a 100644
--- a/llvm/lib/ABI/TargetInfo.cpp
+++ b/llvm/lib/ABI/TargetInfo.cpp
@@ -71,45 +71,6 @@ const Type *TargetInfo::useFirstFieldIfTransparentUnion(const Type *Ty) const {
return Ty;
}
-ArgInfo TargetInfo::classifyDefaultArgumentType(const Type *Ty) const {
- Ty = useFirstFieldIfTransparentUnion(Ty);
-
- if (isAggregateTypeForABI(Ty)) {
- // A record that must stay in memory keeps ByVal, everything else indirect.
- if (RecordArgABI RAA = getRecordArgABI(Ty))
- return getNaturalAlignIndirect(Ty, /*ByVal=*/RAA == RAA_DirectInMemory);
- return getNaturalAlignIndirect(Ty);
- }
-
- if (const auto *IT = dyn_cast<IntegerType>(Ty)) {
- // A _BitInt wider than 128 bits does not fit in registers.
- if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
- return getNaturalAlignIndirect(Ty);
- if (isPromotableInteger(IT))
- return ArgInfo::getExtend(Ty);
- }
-
- return ArgInfo::getDirect();
-}
-
-ArgInfo TargetInfo::classifyDefaultReturnType(const Type *RetTy) const {
- if (RetTy->isVoid())
- return ArgInfo::getIgnore();
-
- if (isAggregateTypeForABI(RetTy))
- return getNaturalAlignIndirect(RetTy);
-
- if (const auto *IT = dyn_cast<IntegerType>(RetTy)) {
- // A _BitInt wider than 128 bits does not fit in registers.
- if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
- return getNaturalAlignIndirect(RetTy);
- if (isPromotableInteger(IT))
- return ArgInfo::getExtend(RetTy);
- }
-
- return ArgInfo::getDirect();
-}
-
bool TargetInfo::maybeCommonClassifyReturnType(FunctionInfo &FI) const {
const abi::Type *Ty = FI.getReturnType();
diff --git a/llvm/unittests/ABI/TargetInfoTest.cpp b/llvm/unittests/ABI/TargetInfoTest.cpp
index acf4842777677..510c966a993dd 100644
--- a/llvm/unittests/ABI/TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/TargetInfoTest.cpp
@@ -6,13 +6,14 @@
//
//===----------------------------------------------------------------------===//
//
-// Tests for target-independent helpers on the TargetInfo base class. These are
+// Tests for the shared default classification in DefaultTargetInfo. These are
// exercised through a minimal concrete target so any new shared default lands
// here rather than in a per-target test file.
//
//===----------------------------------------------------------------------===//
#include "llvm/ABI/TargetInfo.h"
+#include "llvm/ABI/DefaultTargetInfo.h"
#include "llvm/ABI/FunctionInfo.h"
#include "llvm/ABI/Types.h"
#include "llvm/Support/Alignment.h"
@@ -27,25 +28,22 @@ using namespace llvm;
using ABIType = llvm::abi::Type;
using llvm::abi::ArgInfo;
+using llvm::abi::DefaultTargetInfo;
using llvm::abi::FieldInfo;
using llvm::abi::FunctionInfo;
using llvm::abi::RecordFlags;
using llvm::abi::StructPacking;
-using llvm::abi::TargetInfo;
using llvm::abi::TypeBuilder;
-// A minimal concrete target that re-exposes the shared, protected default
-// classifiers so they can be exercised directly, independent of any real
-// target.
-class TestTargetInfo : public TargetInfo {
+// A minimal concrete target that inherits the shared default classifiers so
+// they can be exercised directly, independent of any real target. Only
+// getABICompatInfo() is left to supply.
+class TestTargetInfo : public DefaultTargetInfo {
public:
- explicit TestTargetInfo(TypeBuilder &Builder) : TargetInfo(Builder) {}
- void computeInfo(FunctionInfo &) const override {}
+ explicit TestTargetInfo(TypeBuilder &Builder) : DefaultTargetInfo(Builder) {}
const llvm::abi::ABICompatInfo &getABICompatInfo() const override {
return Compat;
}
- using TargetInfo::classifyDefaultArgumentType;
- using TargetInfo::classifyDefaultReturnType;
private:
llvm::abi::ABICompatInfo Compat;
@@ -86,12 +84,12 @@ class TargetInfoTest : public ::testing::Test {
ArgInfo classifyArg(const ABIType *Ty) {
TestTargetInfo TI(TB);
- return TI.classifyDefaultArgumentType(Ty);
+ return TI.classifyArgumentType(Ty);
}
ArgInfo classifyRet(const ABIType *Ty) {
TestTargetInfo TI(TB);
- return TI.classifyDefaultReturnType(Ty);
+ return TI.classifyReturnType(Ty);
}
};
>From 0c53ebf78c566bdd7c6f2dd8c6b6cfb808a76a0b Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Tue, 22 Sep 2026 12:28:44 +0530
Subject: [PATCH 4/4] [llvm][ABI] Make the _BitInt register threshold
target-driven
---
llvm/include/llvm/ABI/TargetInfo.h | 9 ++++++++
llvm/lib/ABI/DefaultTargetInfo.cpp | 10 +++++----
llvm/lib/ABI/TargetInfo.cpp | 4 ++++
llvm/unittests/ABI/TargetInfoTest.cpp | 30 ++++++++++++++++++++++++++-
4 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h
index 6f7f29e70bb3d..81fef79353e8c 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -80,10 +80,19 @@ class TargetInfo {
/// store a derived object and return that as an ABICompatInfo reference.
virtual const ABICompatInfo &getABICompatInfo() const = 0;
+ /// Whether the target has a 128-bit integer type. Default true.
+ virtual bool hasInt128Type() const { return true; }
+
+ /// Width of `long long` in bits. Default 64.
+ virtual unsigned getLongLongWidth() const { return 64; }
+
protected:
LLVM_ABI RecordArgABI getRecordArgABI(const RecordType *RT) const;
LLVM_ABI RecordArgABI getRecordArgABI(const Type *Ty) const;
LLVM_ABI bool isPromotableInteger(const IntegerType *IT) const;
+
+ /// Bit width above which a _BitInt cannot stay in registers.
+ LLVM_ABI unsigned getBitIntRegThreshold() const;
LLVM_ABI ArgInfo getNaturalAlignIndirect(const Type *Ty,
bool ByVal = true) const;
LLVM_ABI bool isAggregateTypeForABI(const Type *Ty) const;
diff --git a/llvm/lib/ABI/DefaultTargetInfo.cpp b/llvm/lib/ABI/DefaultTargetInfo.cpp
index 01380c106e6a3..4f2b5dbaaa269 100644
--- a/llvm/lib/ABI/DefaultTargetInfo.cpp
+++ b/llvm/lib/ABI/DefaultTargetInfo.cpp
@@ -24,8 +24,9 @@ ArgInfo DefaultTargetInfo::classifyArgumentType(const Type *Ty) const {
}
if (const auto *IT = dyn_cast<IntegerType>(Ty)) {
- // A _BitInt wider than 128 bits does not fit in registers.
- if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
+ // A _BitInt too wide for the largest integer register goes indirect.
+ if (IT->isBitInt() &&
+ IT->getSizeInBits().getFixedValue() > getBitIntRegThreshold())
return getNaturalAlignIndirect(Ty);
if (isPromotableInteger(IT))
return ArgInfo::getExtend(Ty);
@@ -42,8 +43,9 @@ ArgInfo DefaultTargetInfo::classifyReturnType(const Type *RetTy) const {
return getNaturalAlignIndirect(RetTy);
if (const auto *IT = dyn_cast<IntegerType>(RetTy)) {
- // A _BitInt wider than 128 bits does not fit in registers.
- if (IT->isBitInt() && IT->getSizeInBits().getFixedValue() > 128)
+ // A _BitInt too wide for the largest integer register goes indirect.
+ if (IT->isBitInt() &&
+ IT->getSizeInBits().getFixedValue() > getBitIntRegThreshold())
return getNaturalAlignIndirect(RetTy);
if (isPromotableInteger(IT))
return ArgInfo::getExtend(RetTy);
diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp
index 9ad8cb8f5829a..2474b6d95dcd6 100644
--- a/llvm/lib/ABI/TargetInfo.cpp
+++ b/llvm/lib/ABI/TargetInfo.cpp
@@ -41,6 +41,10 @@ bool TargetInfo::isPromotableInteger(const IntegerType *IT) const {
return BitWidth < 32;
}
+unsigned TargetInfo::getBitIntRegThreshold() const {
+ return hasInt128Type() ? 128 : getLongLongWidth();
+}
+
ArgInfo TargetInfo::getNaturalAlignIndirect(const Type *Ty, bool ByVal) const {
return ArgInfo::getIndirect(Ty->getAlignment(), ByVal);
}
diff --git a/llvm/unittests/ABI/TargetInfoTest.cpp b/llvm/unittests/ABI/TargetInfoTest.cpp
index 510c966a993dd..650926e9e40e7 100644
--- a/llvm/unittests/ABI/TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/TargetInfoTest.cpp
@@ -49,6 +49,14 @@ class TestTargetInfo : public DefaultTargetInfo {
llvm::abi::ABICompatInfo Compat;
};
+// A target lacking a 128-bit integer type, so the _BitInt register threshold
+// is the width of `long long`.
+class NoInt128TargetInfo : public TestTargetInfo {
+public:
+ using TestTargetInfo::TestTargetInfo;
+ bool hasInt128Type() const override { return false; }
+};
+
class TargetInfoTest : public ::testing::Test {
protected:
llvm::BumpPtrAllocator Alloc;
@@ -58,13 +66,17 @@ class TargetInfoTest : public ::testing::Test {
const ABIType *Void;
/// A _BitInt wider than 128 bits, which cannot be passed in registers.
const ABIType *WideBitInt;
+ /// A _BitInt between `long long` (64) and 128 bits wide.
+ const ABIType *MidBitInt;
TargetInfoTest()
: TB(Alloc), I16(TB.getIntegerType(16, llvm::Align(2), /*Signed=*/true)),
I32(TB.getIntegerType(32, llvm::Align(4), /*Signed=*/true)),
Void(TB.getVoidType()),
WideBitInt(TB.getIntegerType(129, llvm::Align(8), /*Signed=*/true,
- /*IsBitInt=*/true)) {}
+ /*IsBitInt=*/true)),
+ MidBitInt(TB.getIntegerType(100, llvm::Align(8), /*Signed=*/true,
+ /*IsBitInt=*/true)) {}
/// A record with a single int field, passable in registers.
const ABIType *recordInReg() {
@@ -91,6 +103,11 @@ class TargetInfoTest : public ::testing::Test {
TestTargetInfo TI(TB);
return TI.classifyReturnType(Ty);
}
+
+ ArgInfo classifyArgNoInt128(const ABIType *Ty) {
+ NoInt128TargetInfo TI(TB);
+ return TI.classifyArgumentType(Ty);
+ }
};
// --- Argument classification -------------------------------------------------
@@ -125,6 +142,17 @@ TEST_F(TargetInfoTest, DefaultArgWideBitIntIsIndirect) {
EXPECT_TRUE(classifyArg(WideBitInt).isIndirect());
}
+// With a 128-bit integer type, a _BitInt no wider than 128 stays direct.
+TEST_F(TargetInfoTest, DefaultArgMidBitIntIsDirectWithInt128) {
+ EXPECT_TRUE(classifyArg(MidBitInt).isDirect());
+}
+
+// Without a 128-bit integer type the threshold drops to long long (64), so the
+// same _BitInt is passed indirectly.
+TEST_F(TargetInfoTest, DefaultArgMidBitIntIsIndirectWithoutInt128) {
+ EXPECT_TRUE(classifyArgNoInt128(MidBitInt).isIndirect());
+}
+
// A transparent union is classified as its first field, so a union of one int
// is passed directly rather than as an aggregate.
TEST_F(TargetInfoTest, DefaultArgTransparentUnionUsesFirstField) {
More information about the llvm-commits
mailing list