Skip to content

Commit

Permalink
[CIR][Codegen] RTTI support for virtual class inheritence (#259)
Browse files Browse the repository at this point in the history
This patch adds RTTI support for C++ virtual inheritance.

This patch does not include LLVM lowering support.
  • Loading branch information
htyu authored and lanza committed Oct 18, 2023
1 parent 741abf3 commit 1ef58ab
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 17 deletions.
3 changes: 2 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@ def TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> {

The verifier enforces that the output type is always a `!cir.struct`,
and that the ArrayAttr element types match the equivalent member type
for the resulting struct.
for the resulting struct, i.e, a GlobalViewAttr for symbol reference or
an IntAttr for flags.

Example:

Expand Down
150 changes: 146 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Linkage.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/Support/ErrorHandling.h"

using namespace cir;
using namespace clang;
Expand Down Expand Up @@ -825,7 +826,7 @@ class CIRGenItaniumRTTIBuilder {
/// Build an abi::__vmi_class_type_info, used for
/// classes with bases that do not satisfy the abi::__si_class_type_info
/// constraints, according ti the Itanium C++ ABI, 2.9.5p5c.
void BuildVMIClassTypeInfo(const CXXRecordDecl *RD);
void BuildVMIClassTypeInfo(mlir::Location loc, const CXXRecordDecl *RD);

// /// Build an abi::__pointer_type_info struct, used
// /// for pointer types.
Expand Down Expand Up @@ -1437,8 +1438,149 @@ void CIRGenItaniumRTTIBuilder::BuildSIClassTypeInfo(mlir::Location loc,
Fields.push_back(BaseTypeInfo);
}

void CIRGenItaniumRTTIBuilder::BuildVMIClassTypeInfo(const CXXRecordDecl *RD) {
// TODO: Implement this function.
namespace {
/// Contains virtual and non-virtual bases seen when traversing a class
/// hierarchy.
struct SeenBases {
llvm::SmallPtrSet<const CXXRecordDecl *, 16> NonVirtualBases;
llvm::SmallPtrSet<const CXXRecordDecl *, 16> VirtualBases;
};
} // namespace

/// Compute the value of the flags member in abi::__vmi_class_type_info.
///
static unsigned ComputeVMIClassTypeInfoFlags(const CXXBaseSpecifier *Base,
SeenBases &Bases) {

unsigned Flags = 0;

auto *BaseDecl =
cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl());

if (Base->isVirtual()) {
// Mark the virtual base as seen.
if (!Bases.VirtualBases.insert(BaseDecl).second) {
// If this virtual base has been seen before, then the class is diamond
// shaped.
Flags |= CIRGenItaniumRTTIBuilder::VMI_DiamondShaped;
} else {
if (Bases.NonVirtualBases.count(BaseDecl))
Flags |= CIRGenItaniumRTTIBuilder::VMI_NonDiamondRepeat;
}
} else {
// Mark the non-virtual base as seen.
if (!Bases.NonVirtualBases.insert(BaseDecl).second) {
// If this non-virtual base has been seen before, then the class has non-
// diamond shaped repeated inheritance.
Flags |= CIRGenItaniumRTTIBuilder::VMI_NonDiamondRepeat;
} else {
if (Bases.VirtualBases.count(BaseDecl))
Flags |= CIRGenItaniumRTTIBuilder::VMI_NonDiamondRepeat;
}
}

// Walk all bases.
for (const auto &I : BaseDecl->bases())
Flags |= ComputeVMIClassTypeInfoFlags(&I, Bases);

return Flags;
}

static unsigned ComputeVMIClassTypeInfoFlags(const CXXRecordDecl *RD) {
unsigned Flags = 0;
SeenBases Bases;

// Walk all bases.
for (const auto &I : RD->bases())
Flags |= ComputeVMIClassTypeInfoFlags(&I, Bases);

return Flags;
}

/// Build an abi::__vmi_class_type_info, used for
/// classes with bases that do not satisfy the abi::__si_class_type_info
/// constraints, according to the Itanium C++ ABI, 2.9.5p5c.
void CIRGenItaniumRTTIBuilder::BuildVMIClassTypeInfo(mlir::Location loc,
const CXXRecordDecl *RD) {
auto UnsignedIntLTy =
CGM.getTypes().ConvertType(CGM.getASTContext().UnsignedIntTy);
// Itanium C++ ABI 2.9.5p6c:
// __flags is a word with flags describing details about the class
// structure, which may be referenced by using the __flags_masks
// enumeration. These flags refer to both direct and indirect bases.
unsigned Flags = ComputeVMIClassTypeInfoFlags(RD);
Fields.push_back(mlir::cir::IntAttr::get(UnsignedIntLTy, Flags));

// Itanium C++ ABI 2.9.5p6c:
// __base_count is a word with the number of direct proper base class
// descriptions that follow.
Fields.push_back(mlir::cir::IntAttr::get(UnsignedIntLTy, RD->getNumBases()));

if (!RD->getNumBases())
return;

// Now add the base class descriptions.

// Itanium C++ ABI 2.9.5p6c:
// __base_info[] is an array of base class descriptions -- one for every
// direct proper base. Each description is of the type:
//
// struct abi::__base_class_type_info {
// public:
// const __class_type_info *__base_type;
// long __offset_flags;
//
// enum __offset_flags_masks {
// __virtual_mask = 0x1,
// __public_mask = 0x2,
// __offset_shift = 8
// };
// };

// If we're in mingw and 'long' isn't wide enough for a pointer, use 'long
// long' instead of 'long' for __offset_flags. libstdc++abi uses long long on
// LLP64 platforms.
// FIXME: Consider updating libc++abi to match, and extend this logic to all
// LLP64 platforms.
QualType OffsetFlagsTy = CGM.getASTContext().LongTy;
const TargetInfo &TI = CGM.getASTContext().getTargetInfo();
if (TI.getTriple().isOSCygMing() &&
TI.getPointerWidth(LangAS::Default) > TI.getLongWidth())
OffsetFlagsTy = CGM.getASTContext().LongLongTy;
auto OffsetFlagsLTy = CGM.getTypes().ConvertType(OffsetFlagsTy);

for (const auto &Base : RD->bases()) {
// The __base_type member points to the RTTI for the base type.
Fields.push_back(
CIRGenItaniumRTTIBuilder(CXXABI, CGM).BuildTypeInfo(loc, Base.getType()));

auto *BaseDecl =
cast<CXXRecordDecl>(Base.getType()->castAs<RecordType>()->getDecl());

int64_t OffsetFlags = 0;

// All but the lower 8 bits of __offset_flags are a signed offset.
// For a non-virtual base, this is the offset in the object of the base
// subobject. For a virtual base, this is the offset in the virtual table of
// the virtual base offset for the virtual base referenced (negative).
CharUnits Offset;
if (Base.isVirtual())
Offset = CGM.getItaniumVTableContext().getVirtualBaseOffsetOffset(
RD, BaseDecl);
else
llvm_unreachable("Multi-inheritence NYI");

OffsetFlags = uint64_t(Offset.getQuantity()) << 8;

// The low-order byte of __offset_flags contains flags, as given by the
// masks from the enumeration __offset_flags_masks.
if (Base.isVirtual())
OffsetFlags |= BCTI_Virtual;
if (Base.getAccessSpecifier() == AS_public)
OffsetFlags |= BCTI_Public;

Fields.push_back(mlir::cir::IntAttr::get(OffsetFlagsLTy, OffsetFlags));
}
}

mlir::Attribute
Expand Down Expand Up @@ -1565,7 +1707,7 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::BuildTypeInfo(
if (CanUseSingleInheritance(RD)) {
BuildSIClassTypeInfo(loc, RD);
} else {
BuildVMIClassTypeInfo(RD);
BuildVMIClassTypeInfo(loc, RD);
}

break;
Expand Down
15 changes: 8 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,15 +501,16 @@ void CIRGenVTables::buildVTTDefinition(mlir::cir::GlobalOp VTT,
}

mlir::Attribute Idxs[3] = {
CGM.getBuilder().getI32IntegerAttr(0),
CGM.getBuilder().getI32IntegerAttr(AddressPoint.VTableIndex),
CGM.getBuilder().getI32IntegerAttr(AddressPoint.AddressPointIndex),
mlir::cir::IntAttr::get(CGM.getBuilder().getSInt32Ty(), 0),
mlir::cir::IntAttr::get(CGM.getBuilder().getSInt32Ty(),
AddressPoint.VTableIndex),
mlir::cir::IntAttr::get(CGM.getBuilder().getSInt32Ty(),
AddressPoint.AddressPointIndex),
};

auto Init = mlir::cir::GlobalViewAttr::get(
CGM.getBuilder().getUInt8PtrTy(),
mlir::FlatSymbolRefAttr::get(VTable.getSymNameAttr()),
mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Idxs));
auto Indices = mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Idxs);
auto Init = CGM.getBuilder().getGlobalViewAttr(
CGM.getBuilder().getUInt8PtrTy(), VTable, Indices);

VTTComponents.push_back(Init);
}
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2333,10 +2333,9 @@ LogicalResult TypeInfoAttr::verify(
return failure();

for (auto &member : typeinfoData) {
auto gview = member.dyn_cast_or_null<GlobalViewAttr>();
if (gview)
if (llvm::isa<GlobalViewAttr, IntAttr>(member))
continue;
emitError() << "expected GlobalViewAttr attribute";
emitError() << "expected GlobalViewAttr or IntAttr attribute";
return failure();
}

Expand Down
20 changes: 18 additions & 2 deletions clang/test/CIR/CodeGen/vbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,24 @@ struct B: virtual A {
void ppp() { B b; }


// Vtable definition for B
// CHECK: cir.global linkonce_odr @_ZTV1B = #cir.vtable<{#cir.const_array<[#cir.ptr<12> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1B> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}>
// CHECK: cir.global linkonce_odr @_ZTT1B = #cir.const_array<[#cir.global_view<@_ZTV1B, [0 : i32, 0 : i32, 3 : i32]> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 1>

// VTT for B.
// CHECK: cir.global linkonce_odr @_ZTT1B = #cir.const_array<[#cir.global_view<@_ZTV1B, [#cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<3> : !s32i]> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 1>

// CHECK: cir.global "private" external @_ZTVN10__cxxabiv121__vmi_class_type_infoE

// Type info name for B
// CHECK: cir.global linkonce_odr @_ZTS1B = #cir.const_array<"1B" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>
// CHECK: cir.global constant external @_ZTI1B = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv121__vmi_class_type_infoE, [#cir.int<2> : !s64i]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1B> : !cir.ptr<!u8i>}>

// CHECK: cir.global "private" external @_ZTVN10__cxxabiv117__class_type_infoE : !cir.ptr<!cir.ptr<!u8i>>

// Type info name for A
// CHECK: cir.global linkonce_odr @_ZTS1A = #cir.const_array<"1A" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>

// Type info A.
// CHECK: cir.global constant external @_ZTI1A = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [#cir.int<2> : !s64i]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1A> : !cir.ptr<!u8i>}>

// Type info B.
// CHECK: cir.global constant external @_ZTI1B = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv121__vmi_class_type_infoE, [#cir.int<2> : !s64i]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1B> : !cir.ptr<!u8i>, #cir.int<0> : !u32i, #cir.int<1> : !u32i, #cir.global_view<@_ZTI1A> : !cir.ptr<!u8i>, #cir.int<-6141> : !s64i}>

0 comments on commit 1ef58ab

Please sign in to comment.