blob: 31d9250a48ec98a35edbc97bbaab139af6ae36c5 [file]
//==---- QualTypeMapper.cpp - Maps Clang QualType to LLVMABI Types ---------==//
//
// 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
/// Maps Clang QualType instances to corresponding LLVM ABI type
/// representations. This mapper translates high-level type information from the
/// AST into low-level ABI-specific types that encode size, alignment, and
/// layout details required for code generation and cross-language
/// interoperability.
///
//===----------------------------------------------------------------------===//
#include "QualTypeMapper.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ABI/Types.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TypeSize.h"
#include <cstdint>
namespace clang {
namespace CodeGen {
/// Main entry point for converting Clang QualType to LLVM ABI Type.
/// This method performs type canonicalization, caching, and dispatches
/// to specialized conversion methods based on the type kind.
///
/// \param QT The Clang QualType to convert
/// \return Corresponding LLVM ABI Type representation
const llvm::abi::Type *QualTypeMapper::convertType(QualType QT) {
// Canonicalize type and strip qualifiers
// This ensures consistent type representation across different contexts
//
// TODO: AttributedType is NeverCanonical, so aligned typedef attributes
// for instance, __attribute__((aligned(N))) are lost here. Capture the
// effective alignment from the original QT and thread it through
// convertTypeImpl.
QT = QT.getCanonicalType().getUnqualifiedType();
// Results are cached since type conversion may be expensive.
auto It = TypeCache.find(QT);
if (It != TypeCache.end())
return It->second;
const llvm::abi::Type *Result = convertTypeImpl(QT);
assert(Result && "convertTypeImpl returned nullptr");
TypeCache[QT] = Result;
return Result;
}
/// Dispatches to specialized conversion methods based on the type kind.
const llvm::abi::Type *QualTypeMapper::convertTypeImpl(QualType QT) {
switch (QT->getTypeClass()) {
// Non-canonical and dependent types should have been stripped by
// getCanonicalType() above or cannot appear during code generation.
#define TYPE(Class, Base)
#define ABSTRACT_TYPE(Class, Base)
#define NON_CANONICAL_TYPE(Class, Base) case Type::Class:
#define DEPENDENT_TYPE(Class, Base) case Type::Class:
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class:
#include "clang/AST/TypeNodes.inc"
llvm::reportFatalInternalError(
"Non-canonical or dependent types should not reach ABI lowering");
case Type::Builtin:
return convertBuiltinType(cast<BuiltinType>(QT));
case Type::Pointer:
return createPointerTypeForPointee(cast<PointerType>(QT)->getPointeeType());
case Type::LValueReference:
case Type::RValueReference:
return createPointerTypeForPointee(
cast<ReferenceType>(QT)->getPointeeType());
case Type::ConstantArray:
case Type::ArrayParameter:
case Type::IncompleteArray:
case Type::VariableArray:
return convertArrayType(cast<ArrayType>(QT));
case Type::Vector:
case Type::ExtVector:
return convertVectorType(cast<VectorType>(QT));
case Type::Record:
return convertRecordType(cast<RecordType>(QT));
case Type::Enum:
return convertEnumType(cast<EnumType>(QT));
case Type::Complex:
return convertComplexType(cast<ComplexType>(QT));
case Type::Atomic:
return convertType(cast<AtomicType>(QT)->getValueType());
case Type::BlockPointer:
case Type::Pipe:
return createPointerTypeForPointee(ASTCtx.VoidPtrTy);
case Type::ConstantMatrix: {
const auto *MT = cast<ConstantMatrixType>(QT);
return Builder.getArrayType(convertType(MT->getElementType()),
MT->getNumRows() * MT->getNumColumns(),
ASTCtx.getTypeSize(QT), /*IsMatrixType=*/true);
}
case Type::MemberPointer:
return convertMemberPointerType(cast<MemberPointerType>(QT));
case Type::BitInt: {
const auto *BIT = cast<BitIntType>(QT);
return Builder.getIntegerType(BIT->getNumBits(), getTypeAlign(QT),
/*Signed=*/BIT->isSigned(),
/*IsBitInt=*/true);
}
case Type::ObjCObject:
case Type::ObjCInterface:
case Type::ObjCObjectPointer:
// Objective-C objects are represented as pointers in the ABI.
return Builder.getPointerType(
ASTCtx.getTargetInfo().getPointerWidth(QT.getAddressSpace()),
llvm::Align(
ASTCtx.getTargetInfo().getPointerAlign(QT.getAddressSpace()) / 8),
ASTCtx.getTargetInfo().getTargetAddressSpace(QT.getAddressSpace()));
case Type::OverflowBehavior:
return convertType(cast<OverflowBehaviorType>(QT)->getUnderlyingType());
case Type::Auto:
case Type::DeducedTemplateSpecialization:
case Type::FunctionProto:
case Type::FunctionNoProto:
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
llvm::reportFatalInternalError("Type not supported in ABI lowering");
}
llvm_unreachable("unhandled type class in convertTypeImpl");
}
/// Converts C/C++ builtin types to LLVM ABI types.
/// This handles all fundamental scalar types including integers, floats,
/// and special types like void and bool.
const llvm::abi::Type *
QualTypeMapper::convertBuiltinType(const BuiltinType *BT) {
QualType QT(BT, 0);
switch (BT->getKind()) {
case BuiltinType::Void:
return Builder.getVoidType();
case BuiltinType::NullPtr:
return createPointerTypeForPointee(QT);
case BuiltinType::Bool:
return Builder.getIntegerType(1, getTypeAlign(QT), /*Signed=*/false,
/*IsBitInt=*/false);
case BuiltinType::Char_S:
case BuiltinType::Char_U:
case BuiltinType::SChar:
case BuiltinType::UChar:
case BuiltinType::WChar_S:
case BuiltinType::WChar_U:
case BuiltinType::Char8:
case BuiltinType::Char16:
case BuiltinType::Char32:
case BuiltinType::Short:
case BuiltinType::UShort:
case BuiltinType::Int:
case BuiltinType::UInt:
case BuiltinType::Long:
case BuiltinType::ULong:
case BuiltinType::LongLong:
case BuiltinType::ULongLong:
case BuiltinType::Int128:
case BuiltinType::UInt128:
return Builder.getIntegerType(ASTCtx.getTypeSize(QT), getTypeAlign(QT),
/*Signed=*/BT->isSignedInteger(),
/*IsBitInt=*/false);
case BuiltinType::Half:
case BuiltinType::Float16:
case BuiltinType::BFloat16:
case BuiltinType::Float:
case BuiltinType::Double:
case BuiltinType::LongDouble:
case BuiltinType::Float128:
return Builder.getFloatType(ASTCtx.getFloatTypeSemantics(QT),
getTypeAlign(QT));
// TODO: IBM 128-bit extended double
case BuiltinType::Ibm128:
llvm::reportFatalInternalError(
"IBM128 is not yet supported in the ABI lowering libary");
// TODO: Fixed-point types
case BuiltinType::ShortAccum:
case BuiltinType::Accum:
case BuiltinType::LongAccum:
case BuiltinType::UShortAccum:
case BuiltinType::UAccum:
case BuiltinType::ULongAccum:
case BuiltinType::ShortFract:
case BuiltinType::Fract:
case BuiltinType::LongFract:
case BuiltinType::UShortFract:
case BuiltinType::UFract:
case BuiltinType::ULongFract:
case BuiltinType::SatShortAccum:
case BuiltinType::SatAccum:
case BuiltinType::SatLongAccum:
case BuiltinType::SatUShortAccum:
case BuiltinType::SatUAccum:
case BuiltinType::SatULongAccum:
case BuiltinType::SatShortFract:
case BuiltinType::SatFract:
case BuiltinType::SatLongFract:
case BuiltinType::SatUShortFract:
case BuiltinType::SatUFract:
case BuiltinType::SatULongFract:
llvm::reportFatalInternalError(
"Fixed Point types not yet implemented in the ABI lowering library");
// OpenCL image types are represented as opaque pointers.
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
case BuiltinType::Id:
#include "clang/Basic/OpenCLImageTypes.def"
// OpenCL extension types are represented as opaque pointers.
#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) case BuiltinType::Id:
#include "clang/Basic/OpenCLExtensionTypes.def"
case BuiltinType::OCLSampler:
case BuiltinType::OCLEvent:
case BuiltinType::OCLClkEvent:
case BuiltinType::OCLQueue:
case BuiltinType::OCLReserveID:
return createPointerTypeForPointee(QT);
// Objective-C builtin types are represented as opaque pointers.
case BuiltinType::ObjCId:
case BuiltinType::ObjCClass:
case BuiltinType::ObjCSel:
return createPointerTypeForPointee(QT);
// Target-specific vector/matrix types — not yet implemented.
#define SVE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/AArch64ACLETypes.def"
llvm::reportFatalInternalError(
"AArch64 SVE types not yet supported in ABI lowering library");
#define PPC_VECTOR_TYPE(Name, Id, Size) case BuiltinType::Id:
#include "clang/Basic/PPCTypes.def"
llvm::reportFatalInternalError(
"PPC MMA types not yet supported in ABI lowering library");
#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/RISCVVTypes.def"
llvm::reportFatalInternalError(
"RISC-V vector types not yet supported in ABI lowering library");
#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/WebAssemblyReferenceTypes.def"
llvm::reportFatalInternalError("WebAssembly reference types not yet "
"supported in ABI lowering library");
#define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) case BuiltinType::Id:
#include "clang/Basic/AMDGPUTypes.def"
llvm::reportFatalInternalError(
"AMDGPU types not yet supported in ABI lowering library");
#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/HLSLIntangibleTypes.def"
llvm::reportFatalInternalError(
"HLSL intangible types not yet Supported in ABI lowering library");
// Placeholder types should never reach ABI lowering.
#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id:
#define BUILTIN_TYPE(Id, SingletonId)
#include "clang/AST/BuiltinTypes.def"
llvm::reportFatalInternalError(
"Placeholder type should not reach ABI lowering");
case BuiltinType::Dependent:
llvm::reportFatalInternalError(
"Dependent builtin type should not reach ABI lowering");
}
llvm_unreachable("unhandled builtin type kind in convertBuiltinType");
}
/// Converts array types to LLVM ABI array representations.
/// Handles different array kinds: constant arrays, incomplete arrays,
/// and variable-length arrays.
///
/// \param AT The ArrayType to convert
/// \return LLVM ABI ArrayType or PointerType
const llvm::abi::Type *
QualTypeMapper::convertArrayType(const clang::ArrayType *AT) {
const llvm::abi::Type *ElementType = convertType(AT->getElementType());
uint64_t Size = ASTCtx.getTypeSize(AT);
if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
auto NumElements = CAT->getZExtSize();
return Builder.getArrayType(ElementType, NumElements, Size);
}
if (isa<IncompleteArrayType>(AT))
return Builder.getArrayType(ElementType, 0, 0);
if (const auto *VAT = dyn_cast<VariableArrayType>(AT))
return createPointerTypeForPointee(VAT->getPointeeType());
llvm::reportFatalInternalError(
"unexpected array type in ABI lowering (dependent array types should be "
"resolved before reaching this point)");
}
const llvm::abi::Type *QualTypeMapper::convertVectorType(const VectorType *VT) {
const llvm::abi::Type *ElementType = convertType(VT->getElementType());
QualType VectorQualType(VT, 0);
unsigned NElems = VT->getNumElements();
llvm::ElementCount NumElements = llvm::ElementCount::getFixed(NElems);
llvm::Align VectorAlign = getTypeAlign(VectorQualType);
return Builder.getVectorType(ElementType, NumElements, VectorAlign);
}
/// Converts complex types to LLVM ABI complex representations.
/// Complex types consist of two components of the element type
/// (real and imaginary parts).
///
/// \param CT The ComplexType to convert
/// \return LLVM ABI ComplexType with element type and alignment
const llvm::abi::Type *
QualTypeMapper::convertComplexType(const ComplexType *CT) {
const llvm::abi::Type *ElementType = convertType(CT->getElementType());
llvm::Align ComplexAlign = getTypeAlign(QualType(CT, 0));
return Builder.getComplexType(ElementType, ComplexAlign);
}
/// Converts member pointer types to LLVM ABI representations.
/// Member pointers have different layouts depending on whether they
/// point to functions or data members.
///
/// \param MPT The MemberPointerType to convert
/// \return LLVM ABI MemberPointerType
const llvm::abi::Type *
QualTypeMapper::convertMemberPointerType(const clang::MemberPointerType *MPT) {
QualType QT(MPT, 0);
uint64_t Size = ASTCtx.getTypeSize(QT);
llvm::Align Align = getTypeAlign(QT);
bool IsFunctionPointer = MPT->isMemberFunctionPointerType();
return Builder.getMemberPointerType(IsFunctionPointer, Size, Align);
}
/// Converts record types (struct/class/union) to LLVM ABI representations.
/// This is the main dispatch method that handles different record kinds
/// and delegates to specialized converters.
///
/// \param RT The RecordType to convert
/// \return LLVM ABI RecordType
const llvm::abi::Type *QualTypeMapper::convertRecordType(const RecordType *RT) {
const RecordDecl *RD = RT->getDecl()->getDefinition();
if (!RD)
return Builder.getRecordType({}, llvm::TypeSize::getFixed(0),
llvm::Align(1));
if (RD->isUnion())
return convertUnionType(RD);
// Handle C++ classes with base classes
auto *CXXRd = dyn_cast<CXXRecordDecl>(RD);
if (CXXRd && (CXXRd->getNumBases() > 0 || CXXRd->getNumVBases() > 0))
return convertCXXRecordType(CXXRd);
return convertStructType(RD);
}
/// Converts C++ classes with inheritance to LLVM ABI struct representations.
/// This method handles the complex layout of C++ objects including:
/// - Virtual table pointers for polymorphic classes
/// - Base class subobjects (both direct and virtual bases)
/// - Member field layout with proper offsets
///
/// \param RD The C++ record declaration
/// \return LLVM ABI RecordType representing the complete object layout
const llvm::abi::RecordType *
QualTypeMapper::convertCXXRecordType(const CXXRecordDecl *RD) {
const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD);
SmallVector<llvm::abi::FieldInfo, 16> Fields;
SmallVector<llvm::abi::FieldInfo, 8> BaseClasses;
SmallVector<llvm::abi::FieldInfo, 8> VirtualBaseClasses;
// Add vtable pointer for polymorphic classes
if (RD->isPolymorphic()) {
const llvm::abi::Type *VtablePointer =
createPointerTypeForPointee(ASTCtx.VoidPtrTy);
Fields.emplace_back(VtablePointer, 0);
}
for (const auto &Base : RD->bases()) {
if (Base.isVirtual())
continue;
const RecordType *BaseRT = Base.getType()->castAs<RecordType>();
const llvm::abi::Type *BaseType = convertType(Base.getType());
uint64_t BaseOffset =
Layout.getBaseClassOffset(BaseRT->getAsCXXRecordDecl()).getQuantity() *
8;
BaseClasses.emplace_back(BaseType, BaseOffset);
}
for (const auto &VBase : RD->vbases()) {
const RecordType *VBaseRT = VBase.getType()->castAs<RecordType>();
const llvm::abi::Type *VBaseType = convertType(VBase.getType());
uint64_t VBaseOffset =
Layout.getVBaseClassOffset(VBaseRT->getAsCXXRecordDecl())
.getQuantity() *
8;
VirtualBaseClasses.emplace_back(VBaseType, VBaseOffset);
}
computeFieldInfo(RD, Fields, Layout);
llvm::sort(Fields,
[](const llvm::abi::FieldInfo &A, const llvm::abi::FieldInfo &B) {
return A.OffsetInBits < B.OffsetInBits;
});
llvm::TypeSize Size =
llvm::TypeSize::getFixed(Layout.getSize().getQuantity() * 8);
llvm::Align Alignment = llvm::Align(Layout.getAlignment().getQuantity());
llvm::abi::RecordFlags RecFlags = llvm::abi::RecordFlags::IsCXXRecord;
if (RD->isPolymorphic())
RecFlags |= llvm::abi::RecordFlags::IsPolymorphic;
if (RD->canPassInRegisters())
RecFlags |= llvm::abi::RecordFlags::CanPassInRegisters;
if (RD->hasFlexibleArrayMember())
RecFlags |= llvm::abi::RecordFlags::HasFlexibleArrayMember;
return Builder.getRecordType(Fields, Size, Alignment,
llvm::abi::StructPacking::Default, BaseClasses,
VirtualBaseClasses, RecFlags);
}
/// Converts enumeration types to their underlying integer representations.
/// This method handles various enum states and falls back to safe defaults
/// when enum information is incomplete or invalid.
///
/// \param ET The EnumType to convert
/// \return LLVM ABI IntegerType representing the enum's underlying type
const llvm::abi::Type *
QualTypeMapper::convertEnumType(const clang::EnumType *ET) {
const EnumDecl *ED = ET->getDecl();
QualType UnderlyingType = ED->getIntegerType();
if (UnderlyingType.isNull())
UnderlyingType = ASTCtx.IntTy;
return convertType(UnderlyingType);
}
/// Converts plain C structs and C++ classes without inheritance.
/// This handles the simpler case where we only need to layout member fields
/// without considering base classes or virtual functions.
///
/// \param RD The RecordDecl to convert
/// \return LLVM ABI RecordType
const llvm::abi::RecordType *
QualTypeMapper::convertStructType(const clang::RecordDecl *RD) {
const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD);
bool IsCXXRecord = isa<CXXRecordDecl>(RD);
SmallVector<llvm::abi::FieldInfo, 16> Fields;
computeFieldInfo(RD, Fields, Layout);
llvm::TypeSize Size =
llvm::TypeSize::getFixed(Layout.getSize().getQuantity() * 8);
llvm::Align Alignment = llvm::Align(Layout.getAlignment().getQuantity());
llvm::abi::RecordFlags RecFlags = llvm::abi::RecordFlags::None;
if (IsCXXRecord)
RecFlags |= llvm::abi::RecordFlags::IsCXXRecord;
if (RD->canPassInRegisters())
RecFlags |= llvm::abi::RecordFlags::CanPassInRegisters;
if (RD->hasFlexibleArrayMember())
RecFlags |= llvm::abi::RecordFlags::HasFlexibleArrayMember;
return Builder.getRecordType(Fields, Size, Alignment,
llvm::abi::StructPacking::Default, {}, {},
RecFlags);
}
/// Converts C union types where all fields occupy the same memory location.
/// The union size is determined by its largest member, and all fields
/// start at offset 0.
///
/// \param RD The RecordDecl representing the union
/// \return LLVM ABI UnionType
const llvm::abi::RecordType *
QualTypeMapper::convertUnionType(const clang::RecordDecl *RD) {
const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD);
SmallVector<llvm::abi::FieldInfo, 16> AllFields;
computeFieldInfo(RD, AllFields, Layout);
llvm::TypeSize Size =
llvm::TypeSize::getFixed(Layout.getSize().getQuantity() * 8);
llvm::Align Alignment = llvm::Align(Layout.getAlignment().getQuantity());
llvm::abi::RecordFlags RecFlags = llvm::abi::RecordFlags::None;
if (RD->hasAttr<TransparentUnionAttr>())
RecFlags |= llvm::abi::RecordFlags::IsTransparent;
if (RD->canPassInRegisters())
RecFlags |= llvm::abi::RecordFlags::CanPassInRegisters;
if (isa<CXXRecordDecl>(RD))
RecFlags |= llvm::abi::RecordFlags::IsCXXRecord;
return Builder.getUnionType(AllFields, Size, Alignment,
llvm::abi::StructPacking::Default, RecFlags);
}
llvm::Align QualTypeMapper::getTypeAlign(QualType QT) const {
return llvm::Align(ASTCtx.getTypeAlignInChars(QT).getQuantity());
}
const llvm::abi::Type *
QualTypeMapper::createPointerTypeForPointee(QualType PointeeType) {
auto AddrSpace = PointeeType.getAddressSpace();
auto PointerSize = ASTCtx.getTargetInfo().getPointerWidth(AddrSpace);
llvm::Align Alignment =
llvm::Align(ASTCtx.getTargetInfo().getPointerAlign(AddrSpace));
// Function types without an explicit address space qualifier use the program
// address space, which may differ from the default data address space on
// targets like AMDGPU.
unsigned TargetAddrSpace =
PointeeType->isFunctionType() && !PointeeType.hasAddressSpace()
? DL.getProgramAddressSpace()
: ASTCtx.getTargetInfo().getTargetAddressSpace(AddrSpace);
return Builder.getPointerType(PointerSize, llvm::Align(Alignment.value() / 8),
TargetAddrSpace);
}
/// Processes the fields of a record (struct/class/union) and populates
/// the Fields vector with FieldInfo objects containing type, offset,
/// and bitfield information.
///
/// \param RD The RecordDecl whose fields to process
/// \param Fields Output vector to populate with field information
/// \param Layout The AST record layout containing field offset information
void QualTypeMapper::computeFieldInfo(
const RecordDecl *RD, SmallVectorImpl<llvm::abi::FieldInfo> &Fields,
const ASTRecordLayout &Layout) {
unsigned FieldIndex = 0;
for (const auto *FD : RD->fields()) {
const llvm::abi::Type *FieldType = convertType(FD->getType());
uint64_t OffsetInBits = Layout.getFieldOffset(FieldIndex);
bool IsBitField = FD->isBitField();
uint64_t BitFieldWidth = 0;
bool IsUnnamedBitField = false;
if (IsBitField) {
BitFieldWidth = FD->getBitWidthValue();
IsUnnamedBitField = FD->isUnnamedBitField();
}
Fields.emplace_back(FieldType, OffsetInBits, IsBitField, BitFieldWidth,
IsUnnamedBitField);
++FieldIndex;
}
}
} // namespace CodeGen
} // namespace clang