diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h new file mode 100644 index 0000000000000..7308fe551b8cf --- /dev/null +++ b/llvm/include/llvm/ABI/Types.h @@ -0,0 +1,452 @@ +//===- ABI/Types.h ----------------------------------------------*- 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 +/// This file defines the type system for the LLVMABI library, which mirrors +/// ABI-relevant aspects of frontend types. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_ABI_TYPES_H +#define LLVM_ABI_TYPES_H + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/TypeSize.h" + +namespace llvm { +namespace abi { + +enum class TypeKind { + Void, + MemberPointer, + Complex, + Integer, + Float, + Pointer, + Array, + Vector, + Record, +}; + +/// Represents the ABI-specific view of a type in LLVM. +/// +/// This abstracts platform and language-specific ABI details from the +/// frontend, providing a consistent interface for the ABI Library. +class Type { +private: + TypeSize getTypeStoreSize() const { + TypeSize StoreSizeInBits = getTypeStoreSizeInBits(); + return {StoreSizeInBits.getKnownMinValue() / 8, + StoreSizeInBits.isScalable()}; + } + TypeSize getTypeStoreSizeInBits() const { + TypeSize BaseSize = getSizeInBits(); + uint64_t AlignedSizeInBits = + alignToPowerOf2(BaseSize.getKnownMinValue(), 8); + return {AlignedSizeInBits, BaseSize.isScalable()}; + } + +protected: + TypeKind Kind; + TypeSize SizeInBits; + Align ABIAlignment; + + Type(TypeKind K, TypeSize SizeInBits, Align ABIAlign) + : Kind(K), SizeInBits(SizeInBits), ABIAlignment(ABIAlign) {} + +public: + TypeKind getKind() const { return Kind; } + TypeSize getSizeInBits() const { return SizeInBits; } + Align getAlignment() const { return ABIAlignment; } + + TypeSize getTypeAllocSize() const { + return alignTo(getTypeStoreSize(), getAlignment().value()); + } + + bool isVoid() const { return Kind == TypeKind::Void; } + bool isInteger() const { return Kind == TypeKind::Integer; } + bool isFloat() const { return Kind == TypeKind::Float; } + bool isPointer() const { return Kind == TypeKind::Pointer; } + bool isArray() const { return Kind == TypeKind::Array; } + bool isVector() const { return Kind == TypeKind::Vector; } + bool isRecord() const { return Kind == TypeKind::Record; } + bool isMemberPointer() const { return Kind == TypeKind::MemberPointer; } + bool isComplex() const { return Kind == TypeKind::Complex; } +}; + +class VoidType : public Type { +public: + VoidType() : Type(TypeKind::Void, TypeSize::getFixed(0), Align(1)) {} + + static bool classof(const Type *T) { return T->getKind() == TypeKind::Void; } +}; + +class ComplexType : public Type { +public: + ComplexType(const Type *ElementType, uint64_t SizeInBits, Align Alignment) + : Type(TypeKind::Complex, TypeSize::getFixed(SizeInBits), Alignment), + ElementType(ElementType) {} + + const Type *getElementType() const { return ElementType; } + + static bool classof(const Type *T) { + return T->getKind() == TypeKind::Complex; + } + +private: + const Type *ElementType; +}; + +class IntegerType : public Type { +private: + bool IsSigned; + bool IsBitInt; + +public: + IntegerType(uint64_t BitWidth, Align ABIAlign, bool IsSigned, + bool IsBitInt = false) + : Type(TypeKind::Integer, TypeSize::getFixed(BitWidth), ABIAlign), + IsSigned(IsSigned), IsBitInt(IsBitInt) {} + + bool isSigned() const { return IsSigned; } + bool isBitInt() const { return IsBitInt; } + bool isBool() const { + return getSizeInBits().getFixedValue() == 1 && !IsBitInt; + } + + static bool classof(const Type *T) { + return T->getKind() == TypeKind::Integer; + } +}; + +class FloatType : public Type { +private: + const fltSemantics *Semantics; + +public: + FloatType(const fltSemantics &FloatSemantics, Align ABIAlign) + : Type(TypeKind::Float, + TypeSize::getFixed(APFloat::getSizeInBits(FloatSemantics)), + ABIAlign), + Semantics(&FloatSemantics) {} + + const fltSemantics *getSemantics() const { return Semantics; } + static bool classof(const Type *T) { return T->getKind() == TypeKind::Float; } +}; + +class PointerLikeType : public Type { +protected: + unsigned AddrSpace; + PointerLikeType(TypeKind K, TypeSize SizeInBits, Align ABIAlign, unsigned AS) + : Type(K, SizeInBits, ABIAlign), AddrSpace(AS) {} + +public: + unsigned getAddrSpace() const { return AddrSpace; } + bool isMemberPointer() const { return getKind() == TypeKind::MemberPointer; } + + static bool classof(const Type *T) { + return T->getKind() == TypeKind::Pointer || + T->getKind() == TypeKind::MemberPointer; + } +}; + +class PointerType : public PointerLikeType { +public: + PointerType(uint64_t Size, Align ABIAlign, unsigned AddressSpace = 0) + : PointerLikeType(TypeKind::Pointer, TypeSize::getFixed(Size), ABIAlign, + AddressSpace) {} + + static bool classof(const Type *T) { + return T->getKind() == TypeKind::Pointer; + } +}; + +class MemberPointerType : public PointerLikeType { +private: + bool IsFunctionPointer; + +public: + MemberPointerType(bool IsFunctionPointer, uint64_t SizeInBits, Align ABIAlign, + unsigned AddressSpace = 0) + : PointerLikeType(TypeKind::MemberPointer, TypeSize::getFixed(SizeInBits), + ABIAlign, AddressSpace), + IsFunctionPointer(IsFunctionPointer) {} + bool isFunctionPointer() const { return IsFunctionPointer; } + + static bool classof(const Type *T) { + return T->getKind() == TypeKind::MemberPointer; + } +}; + +class ArrayType : public Type { +private: + const Type *ElementType; + uint64_t NumElements; + bool IsMatrix; + +public: + ArrayType(const Type *ElementType, uint64_t NumElements, uint64_t SizeInBits, + bool IsMatrixType = false) + : Type(TypeKind::Array, TypeSize::getFixed(SizeInBits), + ElementType->getAlignment()), + ElementType(ElementType), NumElements(NumElements), + IsMatrix(IsMatrixType) {} + + const Type *getElementType() const { return ElementType; } + uint64_t getNumElements() const { return NumElements; } + bool isMatrixType() const { return IsMatrix; } + + static bool classof(const Type *T) { return T->getKind() == TypeKind::Array; } +}; + +class VectorType : public Type { +private: + const Type *ElementType; + ElementCount NumElements; + +public: + VectorType(const Type *ElementType, ElementCount NumElements, Align ABIAlign) + : Type(TypeKind::Vector, + TypeSize(ElementType->getSizeInBits().getFixedValue() * + NumElements.getKnownMinValue(), + NumElements.isScalable()), + ABIAlign), + ElementType(ElementType), NumElements(NumElements) {} + + const Type *getElementType() const { return ElementType; } + ElementCount getNumElements() const { return NumElements; } + + static bool classof(const Type *T) { + return T->getKind() == TypeKind::Vector; + } +}; + +struct FieldInfo { + const Type *FieldType; + uint64_t OffsetInBits; + uint64_t BitFieldWidth; + bool IsBitField; + bool IsUnnamedBitfield; + + FieldInfo(const Type *FieldType, uint64_t OffsetInBits = 0, + bool IsBitField = false, uint64_t BitFieldWidth = 0, + bool IsUnnamedBitField = false) + : FieldType(FieldType), OffsetInBits(OffsetInBits), + BitFieldWidth(BitFieldWidth), IsBitField(IsBitField), + IsUnnamedBitfield(IsUnnamedBitField) {} +}; + +enum class StructPacking { Default, Packed, ExplicitPacking }; + +class RecordType : public Type { +private: + ArrayRef Fields; + ArrayRef BaseClasses; + ArrayRef VirtualBaseClasses; + StructPacking Packing; + struct { + LLVM_PREFERRED_TYPE(bool) unsigned CanPassInRegisters : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsUnion : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsTransparent : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsCXXRecord : 1; + LLVM_PREFERRED_TYPE(bool) unsigned IsPolymorphic : 1; + LLVM_PREFERRED_TYPE(bool) unsigned HasFlexibleArrayMember : 1; + } Flags = {}; + +public: + RecordType(ArrayRef StructFields, ArrayRef Bases, + ArrayRef VBases, TypeSize Size, Align Align, + StructPacking Pack = StructPacking::Default, bool Union = false, + bool CXXRecord = false, bool Polymorphic = false, + bool FlexibleArray = false, bool CanPassInRegs = false, + bool Transparent = false) + : Type(TypeKind::Record, Size, Align), Fields(StructFields), + BaseClasses(Bases), VirtualBaseClasses(VBases), Packing(Pack) { + + Flags.CanPassInRegisters = CanPassInRegs; + Flags.IsUnion = Union; + Flags.IsTransparent = Transparent; + Flags.IsCXXRecord = CXXRecord; + Flags.IsPolymorphic = Polymorphic; + Flags.HasFlexibleArrayMember = FlexibleArray; + } + uint32_t getNumFields() const { return Fields.size(); } + StructPacking getPacking() const { return Packing; } + + bool isUnion() const { return Flags.IsUnion; } + bool isCXXRecord() const { return Flags.IsCXXRecord; } + bool isPolymorphic() const { return Flags.IsPolymorphic; } + bool canPassInRegisters() const { return Flags.CanPassInRegisters; } + bool hasFlexibleArrayMember() const { return Flags.HasFlexibleArrayMember; } + + uint32_t getNumBaseClasses() const { return BaseClasses.size(); } + uint32_t getNumVirtualBaseClasses() const { + return VirtualBaseClasses.size(); + } + bool isTransparentUnion() const { return Flags.IsTransparent; } + ArrayRef getFields() const { return Fields; } + ArrayRef getBaseClasses() const { return BaseClasses; } + ArrayRef getVirtualBaseClasses() const { + return VirtualBaseClasses; + } + + static bool isEmptyForABI(const llvm::abi::Type *Ty) { + const auto *RT = dyn_cast(Ty); + if (!RT) + return false; + + for (const auto &Field : RT->getFields()) { + if (!Field.IsUnnamedBitfield) + return false; + } + + if (RT->isCXXRecord()) { + for (const auto &Base : RT->getBaseClasses()) { + if (!isEmptyForABI(Base.FieldType)) + return false; + } + + for (const auto &VBase : RT->getVirtualBaseClasses()) { + if (!isEmptyForABI(VBase.FieldType)) + return false; + } + } + + return true; + } +}; + +/// TypeBuilder manages the lifecycle of ABI types using bump pointer +/// allocation. Types created by a TypeBuilder are valid for the lifetime of the +/// builder. +/// +/// Example usage: +/// \code +/// BumpPtrAllocator Alloc; +/// TypeBuilder Builder(Alloc); +/// const auto *IntTy = Builder.getIntegerType(32, Align(4), true); +/// \endcode +class TypeBuilder { +private: + BumpPtrAllocator &Allocator; + +public: + explicit TypeBuilder(BumpPtrAllocator &Alloc) : Allocator(Alloc) {} + + const VoidType *getVoidType() { + return new (Allocator.Allocate()) VoidType(); + } + + const IntegerType *getIntegerType(uint64_t BitWidth, Align Align, bool Signed, + bool IsBitInt = false) { + return new (Allocator.Allocate()) + IntegerType(BitWidth, Align, Signed, IsBitInt); + } + + const FloatType *getFloatType(const fltSemantics &Semantics, Align Align) { + return new (Allocator.Allocate()) FloatType(Semantics, Align); + } + + const PointerType *getPointerType(uint64_t Size, Align Align, + unsigned Addrspace = 0) { + return new (Allocator.Allocate()) + PointerType(Size, Align, Addrspace); + } + + const ArrayType *getArrayType(const Type *ElementType, uint64_t NumElements, + uint64_t SizeInBits, + bool IsMatrixType = false) { + return new (Allocator.Allocate()) + ArrayType(ElementType, NumElements, SizeInBits, IsMatrixType); + } + + const VectorType *getVectorType(const Type *ElementType, + ElementCount NumElements, Align Align) { + return new (Allocator.Allocate()) + VectorType(ElementType, NumElements, Align); + } + + const RecordType * + getRecordType(ArrayRef Fields, TypeSize Size, Align Align, + StructPacking Pack = StructPacking::Default, + ArrayRef BaseClasses = {}, + ArrayRef VirtualBaseClasses = {}, + bool CXXRecord = false, bool Polymorphic = false, + bool FlexibleArray = false, bool CanPassInRegister = false) { + FieldInfo *FieldArray = Allocator.Allocate(Fields.size()); + std::copy(Fields.begin(), Fields.end(), FieldArray); + + FieldInfo *BaseArray = nullptr; + if (!BaseClasses.empty()) { + BaseArray = Allocator.Allocate(BaseClasses.size()); + std::copy(BaseClasses.begin(), BaseClasses.end(), BaseArray); + } + + FieldInfo *VBaseArray = nullptr; + if (!VirtualBaseClasses.empty()) { + VBaseArray = Allocator.Allocate(VirtualBaseClasses.size()); + std::copy(VirtualBaseClasses.begin(), VirtualBaseClasses.end(), + VBaseArray); + } + + ArrayRef FieldsRef(FieldArray, Fields.size()); + ArrayRef BasesRef(BaseArray, BaseClasses.size()); + ArrayRef VBasesRef(VBaseArray, VirtualBaseClasses.size()); + + return new (Allocator.Allocate()) + RecordType(FieldsRef, BasesRef, VBasesRef, Size, Align, Pack, false, + CXXRecord, Polymorphic, FlexibleArray, CanPassInRegister); + } + + const RecordType *getUnionType(ArrayRef Fields, TypeSize Size, + Align Align, + StructPacking Pack = StructPacking::Default, + bool IsTransparent = false, + bool CanPassInRegs = false, + bool CXXRecord = false) { + FieldInfo *FieldArray = Allocator.Allocate(Fields.size()); + + for (size_t I = 0; I < Fields.size(); ++I) { + const FieldInfo &Field = Fields[I]; + new (&FieldArray[I]) + FieldInfo(Field.FieldType, 0, Field.IsBitField, Field.BitFieldWidth, + Field.IsUnnamedBitfield); + } + + ArrayRef FieldsRef(FieldArray, Fields.size()); + + return new (Allocator.Allocate()) RecordType( + FieldsRef, ArrayRef(), ArrayRef(), Size, Align, + Pack, true, CXXRecord, false, false, CanPassInRegs, IsTransparent); + } + + const ComplexType *getComplexType(const Type *ElementType, Align Align) { + // Complex types have two elements (real and imaginary parts) + uint64_t ElementSize = ElementType->getSizeInBits().getFixedValue(); + uint64_t ComplexSize = ElementSize * 2; + + return new (Allocator.Allocate()) + ComplexType(ElementType, ComplexSize, Align); + } + + const MemberPointerType *getMemberPointerType(bool IsFunctionPointer, + uint64_t SizeInBits, + Align Align) { + return new (Allocator.Allocate()) + MemberPointerType(IsFunctionPointer, SizeInBits, Align); + } +}; + +} // namespace abi +} // namespace llvm + +#endif