Author: Akshay K Date: 2026-09-15T16:06:51-04:00 New Revision: a84250b63d2a731da76a25e091bb9c5374651625
URL: https://github.com/llvm/llvm-project/commit/a84250b63d2a731da76a25e091bb9c5374651625 DIFF: https://github.com/llvm/llvm-project/commit/a84250b63d2a731da76a25e091bb9c5374651625.diff LOG: [LLVMABI] Add CanBeFlattened to abi::ArgInfo (#220558) Mirror `clang::CodeGen::ABIArgInfo::CanBeFlattened` on `abi::ArgInfo` so a classifier can keep a Direct record coercion in one piece, as AAPCS-VFP homogeneous aggregates, AMDGPU direct aggregates and x86 vectorcall HVAs require. The bit defaults to true, so existing classifiers are unchanged. It is exposed through `getCanBeFlattened()` and a chainable `setCanBeFlattened()`, both Direct-only like the classic accessor. The MLIR `ArgClassification::canFlatten` and the CIR rewriter already honour the flag; the CIR bridge will read the new bit with the first classifier that clears it. Added: llvm/unittests/ABI/FunctionInfoTest.cpp Modified: clang/lib/CodeGen/CGCall.cpp llvm/include/llvm/ABI/FunctionInfo.h llvm/unittests/ABI/CMakeLists.txt llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 1221829871b9f..13036b4cdd58c 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -976,6 +976,10 @@ void CodeGenModule::computeABIInfoUsingLib(CGFunctionInfo &FI) { CheckSimple(Target.getDirectAlign(), Res.getDirectAlign(), "DirectAlign"); CheckSimple(Target.getDirectOffset(), Res.getDirectOffset(), "DirectOffset"); + // Extend falls through to here, and only Direct carries the flag. + if (Res.isDirect()) + CheckSimple(Target.getCanBeFlattened(), Res.getCanBeFlattened(), + "CanBeFlattened"); break; case ABIArgInfo::Indirect: CheckSimple(Target.getIndirectByVal(), Res.getIndirectByVal(), @@ -1023,7 +1027,9 @@ ABIArgInfo CodeGenModule::convertABIArgInfo(const llvm::abi::ArgInfo &AbiInfo, CoercedType = AbiReverseMapper->convertType(AbiInfo.getCoerceToType()); if (!CoercedType) CoercedType = getTypes().ConvertType(Type); - return ABIArgInfo::getDirect(CoercedType, AbiInfo.getDirectOffset()); + return ABIArgInfo::getDirect(CoercedType, AbiInfo.getDirectOffset(), + /*Padding=*/nullptr, + AbiInfo.getCanBeFlattened()); } case llvm::abi::ArgInfo::Extend: { llvm::Type *CoercedType = nullptr; diff --git a/llvm/include/llvm/ABI/FunctionInfo.h b/llvm/include/llvm/ABI/FunctionInfo.h index caedafcbe9d22..d2e6c619a0434 100644 --- a/llvm/include/llvm/ABI/FunctionInfo.h +++ b/llvm/include/llvm/ABI/FunctionInfo.h @@ -71,10 +71,11 @@ class ArgInfo { bool ZeroExt : 1; bool IndirectByVal : 1; bool IndirectRealign : 1; + bool CanBeFlattened : 1; ArgInfo(Kind K = Direct) : TheKind(K), SignExt(false), ZeroExt(false), IndirectByVal(false), - IndirectRealign(false) {} + IndirectRealign(false), CanBeFlattened(false) {} public: /// \param T The type to coerce to. If null, the argument's original type is @@ -85,12 +86,16 @@ class ArgInfo { /// return value on x86-64). /// \param Align Override for the argument's alignment. If absent, the /// default alignment for \p T is used. + /// \param CanBeFlattened Whether a record coercion may be split into one + /// wire argument per field. See getCanBeFlattened. static ArgInfo getDirect(const Type *T = nullptr, unsigned Offset = 0, - MaybeAlign Align = std::nullopt) { + MaybeAlign Align = std::nullopt, + bool CanBeFlattened = true) { ArgInfo AI(Direct); AI.CoercionType = T; AI.Alignment = Align; AI.DirectAttr.Offset = Offset; + AI.CanBeFlattened = CanBeFlattened; return AI; } @@ -140,6 +145,13 @@ class ArgInfo { return *this; } + /// See getCanBeFlattened. + ArgInfo &setCanBeFlattened(bool Flatten) { + assert(isDirect() && "Invalid Kind!"); + CanBeFlattened = Flatten; + return *this; + } + Kind getKind() const { return TheKind; } bool isDirect() const { return TheKind == Direct; } bool isIndirect() const { return TheKind == Indirect; } @@ -178,6 +190,13 @@ class ArgInfo { return IndirectRealign; } + /// Whether a Direct record coercion may be split into one wire argument + /// per field. Mirrors clang::CodeGen::ABIArgInfo::CanBeFlattened. + bool getCanBeFlattened() const { + assert(isDirect() && "Invalid Kind!"); + return CanBeFlattened; + } + bool isSignExt() const { assert(isExtend() && "Invalid Kind!"); return SignExt; diff --git a/llvm/unittests/ABI/CMakeLists.txt b/llvm/unittests/ABI/CMakeLists.txt index 40cdd42ea6c61..4a1910ab8809b 100644 --- a/llvm/unittests/ABI/CMakeLists.txt +++ b/llvm/unittests/ABI/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(ABITests AArch64TargetInfoTest.cpp IRTypeMapperTest.cpp + FunctionInfoTest.cpp X86TargetInfoTest.cpp TypesTest.cpp ) diff --git a/llvm/unittests/ABI/FunctionInfoTest.cpp b/llvm/unittests/ABI/FunctionInfoTest.cpp new file mode 100644 index 0000000000000..04fafc1108f83 --- /dev/null +++ b/llvm/unittests/ABI/FunctionInfoTest.cpp @@ -0,0 +1,110 @@ +//===- FunctionInfoTest.cpp - ArgInfo and FunctionInfo 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ABI/FunctionInfo.h" +#include "llvm/ABI/Types.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Allocator.h" +#include "gtest/gtest.h" +#include <optional> + +namespace { + +using ABIType = llvm::abi::Type; +using llvm::abi::ArgEntry; +using llvm::abi::ArgInfo; +using llvm::abi::FieldInfo; +using llvm::abi::FunctionInfo; +using llvm::abi::StructPacking; +using llvm::abi::TypeBuilder; + +class FunctionInfoTest : public ::testing::Test { +protected: + llvm::BumpPtrAllocator Alloc; + TypeBuilder TB; + const ABIType *I32; + const ABIType *I64; + /// A two-i64 record: the shape a classifier coerces a 16-byte struct to when + /// it lands in two registers, and so the shape a rewriter may flatten. + const ABIType *TwoI64; + + FunctionInfoTest() + : TB(Alloc), I32(TB.getIntegerType(32, llvm::Align(4), /*Signed=*/true)), + I64(TB.getIntegerType(64, llvm::Align(8), /*Signed=*/true)), + TwoI64(TB.getRecordType({FieldInfo(I64, 0), FieldInfo(I64, 64)}, + llvm::TypeSize::getFixed(128), llvm::Align(8), + StructPacking::Default)) {} +}; + +TEST_F(FunctionInfoTest, DirectCanBeFlattenedByDefault) { + EXPECT_TRUE(ArgInfo::getDirect().getCanBeFlattened()); + EXPECT_TRUE(ArgInfo::getDirect(TwoI64).getCanBeFlattened()); + EXPECT_TRUE(ArgInfo::getDirect(I64, /*Offset=*/8).getCanBeFlattened()); +} + +TEST_F(FunctionInfoTest, SetCanBeFlattenedRoundTrips) { + ArgInfo Info = ArgInfo::getDirect(TwoI64); + EXPECT_EQ(&Info.setCanBeFlattened(false), &Info); + EXPECT_FALSE(Info.getCanBeFlattened()); + // Clearing the flag leaves the rest of the classification alone. + EXPECT_TRUE(Info.isDirect()); + EXPECT_EQ(Info.getCoerceToType(), TwoI64); + EXPECT_EQ(Info.getDirectOffset(), 0u); + + Info.setCanBeFlattened(true); + EXPECT_TRUE(Info.getCanBeFlattened()); +} + +TEST_F(FunctionInfoTest, GetDirectTakesCanBeFlattened) { + // The spelling a classifier uses to keep an aggregate in one piece. + ArgInfo Info = ArgInfo::getDirect(TwoI64, /*Offset=*/0, std::nullopt, + /*CanBeFlattened=*/false); + EXPECT_TRUE(Info.isDirect()); + EXPECT_EQ(Info.getCoerceToType(), TwoI64); + EXPECT_EQ(Info.getDirectOffset(), 0u); + EXPECT_FALSE(Info.getCanBeFlattened()); + + EXPECT_TRUE(ArgInfo::getDirect(TwoI64, /*Offset=*/0, std::nullopt, + /*CanBeFlattened=*/true) + .getCanBeFlattened()); +} + +TEST_F(FunctionInfoTest, CanBeFlattenedSurvivesFunctionInfo) { + std::unique_ptr<FunctionInfo> FI = + FunctionInfo::create(llvm::CallingConv::C, TwoI64, {TwoI64, I32}); + // Both spellings that clear the flag land in the same place. + FI->getReturnInfo() = ArgInfo::getDirect(TwoI64, /*Offset=*/0, std::nullopt, + /*CanBeFlattened=*/false); + FI->getArgInfo(0).Info = ArgInfo::getDirect(TwoI64).setCanBeFlattened(false); + FI->getArgInfo(1).Info = ArgInfo::getDirect(I32); + + const FunctionInfo &ConstFI = *FI; + EXPECT_FALSE(ConstFI.getReturnInfo().getCanBeFlattened()); + EXPECT_FALSE(ConstFI.arguments()[0].Info.getCanBeFlattened()); + EXPECT_TRUE(ConstFI.arguments()[1].Info.getCanBeFlattened()); + + // The flag rides along with the rest of the classification on copy. + ArgEntry Copy = ConstFI.getArgInfo(0); + EXPECT_FALSE(Copy.Info.getCanBeFlattened()); +} + +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) +TEST_F(FunctionInfoTest, CanBeFlattenedIsDirectOnly) { + EXPECT_DEATH((void)ArgInfo::getIgnore().getCanBeFlattened(), "Invalid Kind"); + EXPECT_DEATH((void)ArgInfo::getExtend(I32).getCanBeFlattened(), + "Invalid Kind"); + EXPECT_DEATH((void)ArgInfo::getIndirect(llvm::Align(8), /*ByVal=*/true) + .getCanBeFlattened(), + "Invalid Kind"); + EXPECT_DEATH((void)ArgInfo::getIgnore().setCanBeFlattened(false), + "Invalid Kind"); +} +#endif + +} // namespace diff --git a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn index 43b98bccc447e..4c34fdf7647bd 100644 --- a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn @@ -9,6 +9,7 @@ unittest("ABITests") { sources = [ "AArch64TargetInfoTest.cpp", "IRTypeMapperTest.cpp", + "FunctionInfoTest.cpp", "X86TargetInfoTest.cpp", "TypesTest.cpp", ] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
