llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-backend-amdgpu Author: Arseniy Obolenskiy (aobolensk) <details> <summary>Changes</summary> Deduplicate similar argument classification logic between AMDGPUABIInfo and AMDGCNSPIRVABIInfo into a shared AMDGPUABIInfoCommon template in ABIInfoImpl.h Follow up requested in https://github.com/llvm/llvm-project/pull/216326 --- Patch is 25.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/223259.diff 3 Files Affected: - (modified) clang/lib/CodeGen/ABIInfoImpl.h (+193) - (modified) clang/lib/CodeGen/Targets/AMDGPU.cpp (+5-198) - (modified) clang/lib/CodeGen/Targets/SPIR.cpp (+2-193) ``````````diff diff --git a/clang/lib/CodeGen/ABIInfoImpl.h b/clang/lib/CodeGen/ABIInfoImpl.h index d9d79c6a55ddb..4649060c8e712 100644 --- a/clang/lib/CodeGen/ABIInfoImpl.h +++ b/clang/lib/CodeGen/ABIInfoImpl.h @@ -11,6 +11,7 @@ #include "ABIInfo.h" #include "CGCXXABI.h" +#include "llvm/IR/DerivedTypes.h" namespace clang::CodeGen { @@ -140,6 +141,198 @@ bool isEmptyRecordForLayout(const ASTContext &Context, QualType T); /// it exists. const Type *isSingleElementStruct(QualType T, ASTContext &Context); +/// Shared classification rules for AMDGPU and AMDGCN-SPIR-V, with \p Base as +/// the fallback ABIInfo for non-register-packed cases. +template <typename Base> class AMDGPUABIInfoCommon : public Base { +protected: + static constexpr unsigned MaxNumRegsForArgsRet = 16; // 16 32-bit registers + mutable unsigned NumRegsLeft = 0; + + using Base::Base; + + /// Estimate number of registers the type will use when passed in registers. + uint64_t numRegsForType(QualType Ty) const { + uint64_t NumRegs = 0; + + if (const VectorType *VT = Ty->template getAs<VectorType>()) { + // Compute from the number of elements. The reported size is based on + // the in-memory size, which includes the padding 4th element for + // 3-vectors. + QualType EltTy = VT->getElementType(); + uint64_t EltSize = this->getContext().getTypeSize(EltTy); + + // 16-bit element vectors should be passed as packed. + if (EltSize == 16) + return (VT->getNumElements() + 1) / 2; + + uint64_t EltNumRegs = (EltSize + 31) / 32; + return EltNumRegs * VT->getNumElements(); + } + + if (const auto *RD = Ty->getAsRecordDecl()) { + assert(!RD->hasFlexibleArrayMember()); + + for (const FieldDecl *Field : RD->fields()) + NumRegs += numRegsForType(Field->getType()); + + return NumRegs; + } + + return (this->getContext().getTypeSize(Ty) + 31) / 32; + } + + bool isHomogeneousAggregateBaseType(QualType Ty) const override { + return true; + } + + bool isHomogeneousAggregateSmallEnough(const Type *T, + uint64_t Members) const override { + uint32_t NumRegs = (this->getContext().getTypeSize(T) + 31) / 32; + + // Homogeneous Aggregates may occupy at most 16 registers. + return Members * NumRegs <= MaxNumRegsForArgsRet; + } + + // Coerce scalar pointer arguments from generic pointers to a fixed AS. + llvm::Type *coerceKernelArgumentType(llvm::Type *Ty, unsigned FromAS, + unsigned ToAS) const { + // Single value types. + auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(Ty); + if (PtrTy && PtrTy->getAddressSpace() == FromAS) + return llvm::PointerType::get(Ty->getContext(), ToAS); + return Ty; + } + + ABIArgInfo classifyReturnType(QualType RetTy) const { + if (!isAggregateTypeForABI(RetTy) || + getRecordArgABI(RetTy, this->getCXXABI())) + return Base::classifyReturnType(RetTy); + + // Ignore empty structs/unions. + if (isEmptyRecord(this->getContext(), RetTy, true)) + return ABIArgInfo::getIgnore(); + + // Lower single-element structs to just return a regular value. + if (const Type *SeltTy = isSingleElementStruct(RetTy, this->getContext())) + return ABIArgInfo::getDirect(this->CGT.ConvertType(QualType(SeltTy, 0))); + + if (const auto *RD = RetTy->getAsRecordDecl(); + RD && RD->hasFlexibleArrayMember()) + return Base::classifyReturnType(RetTy); + + // Pack aggregates <= 4 bytes into single VGPR or pair. + uint64_t Size = this->getContext().getTypeSize(RetTy); + if (Size <= 16) + return ABIArgInfo::getDirect( + llvm::Type::getInt16Ty(this->getVMContext())); + + if (Size <= 32) + return ABIArgInfo::getDirect( + llvm::Type::getInt32Ty(this->getVMContext())); + + if (Size <= 64) { + llvm::Type *I32Ty = llvm::Type::getInt32Ty(this->getVMContext()); + return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2)); + } + + if (numRegsForType(RetTy) <= MaxNumRegsForArgsRet) + return ABIArgInfo::getDirect(); + + return Base::classifyReturnType(RetTy); + } + + ABIArgInfo classifyArgumentType(QualType Ty, bool Variadic) const { + assert(NumRegsLeft <= MaxNumRegsForArgsRet && + "register estimate underflow"); + + Ty = useFirstFieldIfTransparentUnion(Ty); + + if (Variadic) { + return ABIArgInfo::getDirect(/*T=*/nullptr, + /*Offset=*/0, + /*Padding=*/nullptr, + /*CanBeFlattened=*/false, + /*Align=*/0); + } + + if (!isAggregateTypeForABI(Ty)) { + ABIArgInfo ArgInfo = Base::classifyArgumentType(Ty); + if (!ArgInfo.isIndirect()) { + uint64_t NumRegs = numRegsForType(Ty); + NumRegsLeft -= std::min(NumRegs, uint64_t{NumRegsLeft}); + } + + return ArgInfo; + } + + // Records with non-trivial destructors/copy-constructors should not be + // passed by value. + if (auto RAA = getRecordArgABI(Ty, this->getCXXABI())) + return this->getNaturalAlignIndirect( + Ty, this->getDataLayout().getAllocaAddrSpace(), + RAA == CGCXXABI::RAA_DirectInMemory); + + // Ignore empty structs/unions. + if (isEmptyRecord(this->getContext(), Ty, true)) + return ABIArgInfo::getIgnore(); + + // Lower single-element structs to just pass a regular value. TODO: We + // could do reasonable-size multiple-element structs too, using + // getExpand(), though watch out for things like bitfields. + if (const Type *SeltTy = isSingleElementStruct(Ty, this->getContext())) + return ABIArgInfo::getDirect(this->CGT.ConvertType(QualType(SeltTy, 0))); + + if (const auto *RD = Ty->getAsRecordDecl(); + RD && RD->hasFlexibleArrayMember()) + return Base::classifyArgumentType(Ty); + + // Pack aggregates <= 8 bytes into single VGPR or pair. + uint64_t Size = this->getContext().getTypeSize(Ty); + if (Size <= 64) { + unsigned NumRegs = (Size + 31) / 32; + NumRegsLeft -= std::min(NumRegsLeft, NumRegs); + + if (Size <= 16) + return ABIArgInfo::getDirect( + llvm::Type::getInt16Ty(this->getVMContext())); + + if (Size <= 32) + return ABIArgInfo::getDirect( + llvm::Type::getInt32Ty(this->getVMContext())); + + // XXX: Should this be i64 instead, and should the limit increase? + llvm::Type *I32Ty = llvm::Type::getInt32Ty(this->getVMContext()); + return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2)); + } + + if (NumRegsLeft > 0) { + uint64_t NumRegs = numRegsForType(Ty); + if (NumRegsLeft >= NumRegs) { + NumRegsLeft -= NumRegs; + return ABIArgInfo::getDirect(); + } + } + + // Use pass-by-reference instead of pass-by-value for struct arguments in + // function ABI. + return ABIArgInfo::getIndirectAliased( + this->getContext().getTypeAlignInChars(Ty), + this->getContext().getTargetAddressSpace(LangAS::opencl_private)); + } + + llvm::FixedVectorType * + getOptimalVectorMemoryType(llvm::FixedVectorType *Ty, + const LangOptions &LangOpt) const override { + // We have legal instructions for 96-bit so 3x32 can be supported. + // FIXME: This check should be a subtarget feature as technically SI + // doesn't support it. + if (Ty->getNumElements() == 3 && + this->getDataLayout().getTypeSizeInBits(Ty) == 96) + return Ty; + return Base::getOptimalVectorMemoryType(Ty, LangOpt); + } +}; + Address EmitVAArgInstr(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, const ABIArgInfo &AI); diff --git a/clang/lib/CodeGen/Targets/AMDGPU.cpp b/clang/lib/CodeGen/Targets/AMDGPU.cpp index 07e2eac39305d..230742255073e 100644 --- a/clang/lib/CodeGen/Targets/AMDGPU.cpp +++ b/clang/lib/CodeGen/Targets/AMDGPU.cpp @@ -22,95 +22,18 @@ using namespace clang::CodeGen; namespace { -class AMDGPUABIInfo final : public DefaultABIInfo { -private: - static const unsigned MaxNumRegsForArgsRet = 16; - - uint64_t numRegsForType(QualType Ty) const; - - bool isHomogeneousAggregateBaseType(QualType Ty) const override; - bool isHomogeneousAggregateSmallEnough(const Type *Base, - uint64_t Members) const override; - - // Coerce HIP scalar pointer arguments from generic pointers to global ones. - llvm::Type *coerceKernelArgumentType(llvm::Type *Ty, unsigned FromAS, - unsigned ToAS) const { - // Single value types. - auto *PtrTy = llvm::dyn_cast<llvm::PointerType>(Ty); - if (PtrTy && PtrTy->getAddressSpace() == FromAS) - return llvm::PointerType::get(Ty->getContext(), ToAS); - return Ty; - } - +class AMDGPUABIInfo final : public AMDGPUABIInfoCommon<DefaultABIInfo> { public: - explicit AMDGPUABIInfo(CodeGen::CodeGenTypes &CGT) : - DefaultABIInfo(CGT) {} + explicit AMDGPUABIInfo(CodeGen::CodeGenTypes &CGT) + : AMDGPUABIInfoCommon(CGT) {} - ABIArgInfo classifyReturnType(QualType RetTy) const; ABIArgInfo classifyKernelArgumentType(QualType Ty) const; - ABIArgInfo classifyArgumentType(QualType Ty, bool Variadic, - unsigned &NumRegsLeft) const; void computeInfo(CGFunctionInfo &FI) const override; RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, AggValueSlot Slot) const override; - - llvm::FixedVectorType * - getOptimalVectorMemoryType(llvm::FixedVectorType *T, - const LangOptions &Opt) const override { - // We have legal instructions for 96-bit so 3x32 can be supported. - // FIXME: This check should be a subtarget feature as technically SI doesn't - // support it. - if (T->getNumElements() == 3 && getDataLayout().getTypeSizeInBits(T) == 96) - return T; - return DefaultABIInfo::getOptimalVectorMemoryType(T, Opt); - } }; -bool AMDGPUABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const { - return true; -} - -bool AMDGPUABIInfo::isHomogeneousAggregateSmallEnough( - const Type *Base, uint64_t Members) const { - uint32_t NumRegs = (getContext().getTypeSize(Base) + 31) / 32; - - // Homogeneous Aggregates may occupy at most 16 registers. - return Members * NumRegs <= MaxNumRegsForArgsRet; -} - -/// Estimate number of registers the type will use when passed in registers. -uint64_t AMDGPUABIInfo::numRegsForType(QualType Ty) const { - uint64_t NumRegs = 0; - - if (const VectorType *VT = Ty->getAs<VectorType>()) { - // Compute from the number of elements. The reported size is based on the - // in-memory size, which includes the padding 4th element for 3-vectors. - QualType EltTy = VT->getElementType(); - uint64_t EltSize = getContext().getTypeSize(EltTy); - - // 16-bit element vectors should be passed as packed. - if (EltSize == 16) - return (VT->getNumElements() + 1) / 2; - - uint64_t EltNumRegs = (EltSize + 31) / 32; - return EltNumRegs * VT->getNumElements(); - } - - if (const auto *RD = Ty->getAsRecordDecl()) { - assert(!RD->hasFlexibleArrayMember()); - - for (const FieldDecl *Field : RD->fields()) { - QualType FieldTy = Field->getType(); - NumRegs += numRegsForType(FieldTy); - } - - return NumRegs; - } - - return (getContext().getTypeSize(Ty) + 31) / 32; -} - void AMDGPUABIInfo::computeInfo(CGFunctionInfo &FI) const { llvm::CallingConv::ID CC = FI.getCallingConvention(); @@ -120,13 +43,13 @@ void AMDGPUABIInfo::computeInfo(CGFunctionInfo &FI) const { unsigned ArgumentIndex = 0; const unsigned numFixedArguments = FI.getNumRequiredArgs(); - unsigned NumRegsLeft = MaxNumRegsForArgsRet; + NumRegsLeft = MaxNumRegsForArgsRet; for (auto &Arg : FI.arguments()) { if (CC == llvm::CallingConv::AMDGPU_KERNEL) { Arg.info = classifyKernelArgumentType(Arg.type); } else { bool FixedArgument = ArgumentIndex++ < numFixedArguments; - Arg.info = classifyArgumentType(Arg.type, !FixedArgument, NumRegsLeft); + Arg.info = classifyArgumentType(Arg.type, !FixedArgument); } } } @@ -140,45 +63,6 @@ RValue AMDGPUABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, CharUnits::fromQuantity(4), AllowHigherAlign, Slot); } -ABIArgInfo AMDGPUABIInfo::classifyReturnType(QualType RetTy) const { - if (isAggregateTypeForABI(RetTy)) { - // Records with non-trivial destructors/copy-constructors should not be - // returned by value. - if (!getRecordArgABI(RetTy, getCXXABI())) { - // Ignore empty structs/unions. - if (isEmptyRecord(getContext(), RetTy, true)) - return ABIArgInfo::getIgnore(); - - // Lower single-element structs to just return a regular value. - if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext())) - return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0))); - - if (const auto *RD = RetTy->getAsRecordDecl(); - RD && RD->hasFlexibleArrayMember()) - return DefaultABIInfo::classifyReturnType(RetTy); - - // Pack aggregates <= 4 bytes into single VGPR or pair. - uint64_t Size = getContext().getTypeSize(RetTy); - if (Size <= 16) - return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext())); - - if (Size <= 32) - return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext())); - - if (Size <= 64) { - llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext()); - return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2)); - } - - if (numRegsForType(RetTy) <= MaxNumRegsForArgsRet) - return ABIArgInfo::getDirect(); - } - } - - // Otherwise just do the default thing. - return DefaultABIInfo::classifyReturnType(RetTy); -} - /// For kernels all parameters are really passed in a special buffer. It doesn't /// make sense to pass anything byval, so everything must be direct. ABIArgInfo AMDGPUABIInfo::classifyKernelArgumentType(QualType Ty) const { @@ -213,83 +97,6 @@ ABIArgInfo AMDGPUABIInfo::classifyKernelArgumentType(QualType Ty) const { return ABIArgInfo::getDirect(LTy, 0, nullptr, false); } -ABIArgInfo AMDGPUABIInfo::classifyArgumentType(QualType Ty, bool Variadic, - unsigned &NumRegsLeft) const { - assert(NumRegsLeft <= MaxNumRegsForArgsRet && "register estimate underflow"); - - Ty = useFirstFieldIfTransparentUnion(Ty); - - if (Variadic) { - return ABIArgInfo::getDirect(/*T=*/nullptr, - /*Offset=*/0, - /*Padding=*/nullptr, - /*CanBeFlattened=*/false, - /*Align=*/0); - } - - if (isAggregateTypeForABI(Ty)) { - // Records with non-trivial destructors/copy-constructors should not be - // passed by value. - if (auto RAA = getRecordArgABI(Ty, getCXXABI())) - return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace(), - RAA == CGCXXABI::RAA_DirectInMemory); - - // Ignore empty structs/unions. - if (isEmptyRecord(getContext(), Ty, true)) - return ABIArgInfo::getIgnore(); - - // Lower single-element structs to just pass a regular value. TODO: We - // could do reasonable-size multiple-element structs too, using getExpand(), - // though watch out for things like bitfields. - if (const Type *SeltTy = isSingleElementStruct(Ty, getContext())) - return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0))); - - if (const auto *RD = Ty->getAsRecordDecl(); - RD && RD->hasFlexibleArrayMember()) - return DefaultABIInfo::classifyArgumentType(Ty); - - // Pack aggregates <= 8 bytes into single VGPR or pair. - uint64_t Size = getContext().getTypeSize(Ty); - if (Size <= 64) { - unsigned NumRegs = (Size + 31) / 32; - NumRegsLeft -= std::min(NumRegsLeft, NumRegs); - - if (Size <= 16) - return ABIArgInfo::getDirect(llvm::Type::getInt16Ty(getVMContext())); - - if (Size <= 32) - return ABIArgInfo::getDirect(llvm::Type::getInt32Ty(getVMContext())); - - // XXX: Should this be i64 instead, and should the limit increase? - llvm::Type *I32Ty = llvm::Type::getInt32Ty(getVMContext()); - return ABIArgInfo::getDirect(llvm::ArrayType::get(I32Ty, 2)); - } - - if (NumRegsLeft > 0) { - uint64_t NumRegs = numRegsForType(Ty); - if (NumRegsLeft >= NumRegs) { - NumRegsLeft -= NumRegs; - return ABIArgInfo::getDirect(); - } - } - - // Use pass-by-reference in stead of pass-by-value for struct arguments in - // function ABI. - return ABIArgInfo::getIndirectAliased( - getContext().getTypeAlignInChars(Ty), - getContext().getTargetAddressSpace(LangAS::opencl_private)); - } - - // Otherwise just do the default thing. - ABIArgInfo ArgInfo = DefaultABIInfo::classifyArgumentType(Ty); - if (!ArgInfo.isIndirect()) { - uint64_t NumRegs = numRegsForType(Ty); - NumRegsLeft -= std::min(NumRegs, uint64_t{NumRegsLeft}); - } - - return ArgInfo; -} - class AMDGPUTargetCodeGenInfo : public TargetCodeGenInfo { public: AMDGPUTargetCodeGenInfo(CodeGenTypes &CGT) diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index e8148d1566f85..7dbcf5e439095 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -48,40 +48,12 @@ class SPIRVABIInfo : public CommonSPIRABIInfo { ABIArgInfo classifyKernelArgumentType(QualType Ty) const; }; -class AMDGCNSPIRVABIInfo : public SPIRVABIInfo { - // TODO: this should be unified / shared with AMDGPU, ideally we'd like to - // re-use AMDGPUABIInfo eventually, rather than duplicate. - static constexpr unsigned MaxNumRegsForArgsRet = 16; // 16 32-bit registers - mutable unsigned NumRegsLeft = 0; - - uint64_t numRegsForType(QualType Ty) const; - - bool isHomogeneousAggregateBaseType(QualType Ty) const override { - return true; - } - bool isHomogeneousAggregateSmallEnough(const Type *Base, - uint64_t Members) const override { - uint32_t NumRegs = (getContext().getTypeSize(Base) + 31) / 32; - - // Homogeneous Aggregates may occupy at most 16 registers. - return Members * NumRegs <= MaxNumRegsForArgsRet; - } - - // Coerce HIP scalar pointer arguments from generic pointers to global ones. - llvm::Type *coerceKernelArgumentType(llvm::Type *Ty, unsigned FromAS, - unsigned ToAS) const; - - ABIArgInfo classifyReturnType(QualType RetTy) const; +class AMDGCNSPIRVABIInfo : public AMDGPUABIInfoCommon<SPIRVABIInfo> { ABIArgInfo classifyKernelArgumentType(QualType Ty) const; - ABIArgInfo classifyArgumentType(QualType Ty, bool Variadic) const; public: - AMDGCNSPIRVABIInfo(CodeGenTypes &CGT) : SPIRVABIInfo(CGT) {} + AMDGCNSPIRVABIInfo(CodeGenTypes &CGT) : AMDGPUABIInfoCommon(CGT) {} void computeInfo(CGFunctionInfo &FI) const override; - - llvm::FixedVectorType * - getOptimalVectorMemoryType(llvm::FixedVectorType *Ty, - const LangOptions &LangOpt) const override; }; } // end anonymous namespace namespace { @@ -209,84 +181,6 @@ RValue SPIRVABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, /*AllowHigherAlign=*/true, Slot); } -uint64_t AMDGCNSPIRVABIInfo::numRegsForType(QualType Ty) const { - // This duplicates the AMDGPUABI computation. - uint64_t NumRegs = 0; - - if (const VectorType *VT = Ty->getAs<VectorType>()) { - // Compute from the number of elements. The reported size is based on the - // in-memory size, which includes the padding 4th element for 3-vectors. - QualType EltTy = VT->getElementType(); - uint64_t EltSize = getContext().getTypeSize(EltTy); - - // 16-bit element vectors should be passed as packed. - if (EltSize == 16) - return (VT->getNumElements() + 1) / 2; - - uint64_t EltNumRegs = (EltSize + 31) / 32; - return EltNumRegs * VT->getNumElements(); - } - - if (const auto *RD = Ty->getAsRecordDecl()) { - assert(!RD->hasFlexibleArrayMember()); - - for (const FieldDecl *Field : RD->fields()) { - QualType FieldTy = Field->... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/223259 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
