diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index b5f2e1a067274..df7ffbb4a2759 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -37,6 +37,12 @@ class CIRGenCXXABI { void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr); + /// Emit the code to initialize hidden members required to handle virtual + /// inheritance, if needed by the ABI. + virtual void + initializeHiddenVirtualInheritanceMembers(CIRGenFunction &cgf, + const CXXRecordDecl *rd) {} + /// Emit a single constructor/destructor with the gen type from a C++ /// constructor/destructor Decl. virtual void emitCXXStructor(clang::GlobalDecl gd) = 0; diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 9106e5e4824ff..9a27932c12dff 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -222,13 +222,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, const CXXRecordDecl *classDecl = cd->getParent(); - // This code doesn't use range-based iteration because we may need to emit - // code between the virtual base initializers and the non-virtual base or - // between the non-virtual base initializers and the member initializers. - CXXConstructorDecl::init_const_iterator b = cd->init_begin(), - e = cd->init_end(); - - // Virtual base initializers first, if any. They aren't needed if: + // Virtual base initializers aren't needed if: // - This is a base ctor variant // - There are no vbases // - The class is abstract, so a complete object of it cannot be constructed @@ -238,31 +232,60 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, bool constructVBases = ctorType != Ctor_Base && classDecl->getNumVBases() != 0 && !classDecl->isAbstract(); - if (constructVBases) { - cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base"); - return; - } - - const mlir::Value oldThisValue = cxxThisValue; - if (!constructVBases && b != e && (*b)->isBaseInitializer() && - (*b)->isBaseVirtual()) { + if (constructVBases && + !cgm.getTarget().getCXXABI().hasConstructorVariants()) { cgm.errorNYI(cd->getSourceRange(), - "emitCtorPrologue: virtual base initializer"); + "emitCtorPrologue: virtual base without variants"); return; } - // Handle non-virtual base initializers. - for (; b != e && (*b)->isBaseInitializer(); b++) { - assert(!(*b)->isBaseVirtual()); + // Create three separate ranges for the different types of initializers. + auto allInits = cd->inits(); + + // Find the boundaries between the three groups. + auto virtualBaseEnd = std::find_if( + allInits.begin(), allInits.end(), [](const CXXCtorInitializer *Init) { + return !(Init->isBaseInitializer() && Init->isBaseVirtual()); + }); + + auto nonVirtualBaseEnd = std::find_if(virtualBaseEnd, allInits.end(), + [](const CXXCtorInitializer *Init) { + return !Init->isBaseInitializer(); + }); + + // Create the three ranges. + auto virtualBaseInits = llvm::make_range(allInits.begin(), virtualBaseEnd); + auto nonVirtualBaseInits = + llvm::make_range(virtualBaseEnd, nonVirtualBaseEnd); + auto memberInits = llvm::make_range(nonVirtualBaseEnd, allInits.end()); + + const mlir::Value oldThisValue = cxxThisValue; + auto emitInitializer = [&](CXXCtorInitializer *baseInit) { if (cgm.getCodeGenOpts().StrictVTablePointers && cgm.getCodeGenOpts().OptimizationLevel > 0 && - isInitializerOfDynamicClass(*b)) { + isInitializerOfDynamicClass(baseInit)) { + // It's OK to continue after emitting the error here. The missing code + // just "launders" the 'this' pointer. cgm.errorNYI(cd->getSourceRange(), - "emitCtorPrologue: strict vtable pointers"); - return; + "emitCtorPrologue: strict vtable pointers for vbase"); } - emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, *b); + emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, baseInit); + }; + + // Process virtual base initializers. + for (CXXCtorInitializer *virtualBaseInit : virtualBaseInits) { + if (!constructVBases) + continue; + emitInitializer(virtualBaseInit); + } + + assert(!cir::MissingFeatures::msabi()); + + // Then, non-virtual base initializers. + for (CXXCtorInitializer *nonVirtualBaseInit : nonVirtualBaseInits) { + assert(!nonVirtualBaseInit->isBaseVirtual()); + emitInitializer(nonVirtualBaseInit); } cxxThisValue = oldThisValue; @@ -276,8 +299,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, // lowering or optimization phases to keep the memory accesses more // explicit. For now, we don't insert memcpy at all. assert(!cir::MissingFeatures::ctorMemcpyizer()); - for (; b != e; b++) { - CXXCtorInitializer *member = (*b); + for (CXXCtorInitializer *member : memberInits) { assert(!member->isBaseInitializer()); assert(member->isAnyMemberInitializer() && "Delegating initializer on non-delegating constructor"); @@ -370,7 +392,7 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc, initializeVTablePointer(loc, vptr); if (rd->getNumVBases()) - cgm.errorNYI(loc, "initializeVTablePointers: virtual base"); + cgm.getCXXABI().initializeHiddenVirtualInheritanceMembers(*this, rd); } CIRGenFunction::VPtrsVector @@ -418,8 +440,17 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base, const CXXRecordDecl *nextBaseDecl; if (nextBase.isVirtual()) { - cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: virtual base"); - return; + // Check if we've visited this virtual base before. + if (!vbases.insert(baseDecl).second) + continue; + + const ASTRecordLayout &layout = + getContext().getASTRecordLayout(vtableClass); + + nextBaseDecl = nearestVBase; + baseOffset = layout.getVBaseClassOffset(baseDecl); + baseOffsetFromNearestVBase = CharUnits::Zero(); + baseDeclIsNonVirtualPrimaryBase = false; } else { const ASTRecordLayout &layout = getContext().getASTRecordLayout(rd); diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1afac6dd52c2d..469879371eb1d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1972,12 +1972,8 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e, delegating = true; break; case CXXConstructionKind::VirtualBase: - // This should just set 'forVirtualBase' to true and fall through, but - // virtual base class support is otherwise missing, so this needs to wait - // until it can be tested. - cgm.errorNYI(e->getSourceRange(), - "emitCXXConstructExpr: virtual base constructor"); - return; + forVirtualBase = true; + [[fallthrough]]; case CXXConstructionKind::NonVirtualBase: type = Ctor_Base; break; diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index aaf7dc767d888..4fd5a278e1a99 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -487,9 +487,10 @@ mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructor( CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass, clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase) { - if (base.getBase()->getNumVBases()) { + if ((base.getBase()->getNumVBases() || nearestVBase != nullptr) && + needsVTTParameter(cgf.curGD)) { cgm.errorNYI(cgf.curFuncDecl->getLocation(), - "getVTableAddressPointInStructor: virtual base"); + "getVTableAddressPointInStructorWithVTT"); } return getVTableAddressPoint(base, vtableClass); } diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h index b28afe42c39a0..914ef16c2a5ee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h @@ -141,6 +141,10 @@ class CIRGenRecordLayout { // for both virtual and non-virtual bases. llvm::DenseMap nonVirtualBases; + /// Map from virtual bases to their field index in the complete object. + llvm::DenseMap + completeObjectVirtualBases; + /// Map from (bit-field) record field to the corresponding CIR record type /// field no. This info is populated by record builder. llvm::DenseMap bitFields; diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 1764967329969..6c7cf75aa2c99 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -41,7 +41,7 @@ struct CIRRecordLowering final { // member type that ensures correct rounding. struct MemberInfo final { CharUnits offset; - enum class InfoKind { VFPtr, Field, Base } kind; + enum class InfoKind { VFPtr, Field, Base, VBase } kind; mlir::Type data; union { const FieldDecl *fieldDecl; @@ -71,17 +71,18 @@ struct CIRRecordLowering final { void setBitFieldInfo(const FieldDecl *fd, CharUnits startOffset, mlir::Type storageType); - void lower(); + void lower(bool NonVirtualBaseType); void lowerUnion(); /// Determines if we need a packed llvm struct. - void determinePacked(); + void determinePacked(bool nvBaseType); /// Inserts padding everywhere it's needed. void insertPadding(); void computeVolatileBitfields(); - void accumulateBases(const CXXRecordDecl *cxxRecordDecl); + void accumulateBases(); void accumulateVPtrs(); + void accumulateVBases(); void accumulateFields(); RecordDecl::field_iterator accumulateBitFields(RecordDecl::field_iterator field, @@ -96,6 +97,17 @@ struct CIRRecordLowering final { /// Helper function to check if the target machine is BigEndian. bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); } + // The Itanium base layout rule allows virtual bases to overlap + // other bases, which complicates layout in specific ways. + // + // Note specifically that the ms_struct attribute doesn't change this. + bool isOverlappingVBaseABI() { + return !astContext.getTargetInfo().getCXXABI().isMicrosoft(); + } + // Recursively searches all of the bases to find out if a vbase is + // not the primary vbase of some base class. + bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query); + CharUnits bitsToCharUnits(uint64_t bitOffset) { return astContext.toCharUnitsFromBits(bitOffset); } @@ -184,6 +196,7 @@ struct CIRRecordLowering final { CIRGenBuilderTy &builder; const ASTContext &astContext; const RecordDecl *recordDecl; + const CXXRecordDecl *cxxRecordDecl; const ASTRecordLayout &astRecordLayout; // Helpful intermediate data-structures std::vector members; @@ -192,6 +205,7 @@ struct CIRRecordLowering final { llvm::DenseMap bitFields; llvm::DenseMap fieldIdxMap; llvm::DenseMap nonVirtualBases; + llvm::DenseMap virtualBases; cir::CIRDataLayout dataLayout; LLVM_PREFERRED_TYPE(bool) @@ -211,13 +225,14 @@ struct CIRRecordLowering final { CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl, bool packed) - : cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()), - astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl), - astRecordLayout( - cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)), - dataLayout(cirGenTypes.getCGModule().getModule()), - zeroInitializable(true), zeroInitializableAsBase(true), packed(packed), - padded(false) {} + : cirGenTypes{cirGenTypes}, builder{cirGenTypes.getBuilder()}, + astContext{cirGenTypes.getASTContext()}, recordDecl{recordDecl}, + cxxRecordDecl{llvm::dyn_cast(recordDecl)}, + astRecordLayout{ + cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)}, + dataLayout{cirGenTypes.getCGModule().getModule()}, + zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed}, + padded{false} {} void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd, CharUnits startOffset, @@ -246,27 +261,28 @@ void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd, info.volatileStorageOffset = CharUnits::Zero(); } -void CIRRecordLowering::lower() { +void CIRRecordLowering::lower(bool nonVirtualBaseType) { if (recordDecl->isUnion()) { lowerUnion(); computeVolatileBitfields(); return; } - assert(!cir::MissingFeatures::recordLayoutVirtualBases()); - CharUnits size = astRecordLayout.getSize(); + CharUnits size = nonVirtualBaseType ? astRecordLayout.getNonVirtualSize() + : astRecordLayout.getSize(); accumulateFields(); - if (const auto *cxxRecordDecl = dyn_cast(recordDecl)) { + if (cxxRecordDecl) { accumulateVPtrs(); - accumulateBases(cxxRecordDecl); + accumulateBases(); if (members.empty()) { appendPaddingBytes(size); computeVolatileBitfields(); return; } - assert(!cir::MissingFeatures::recordLayoutVirtualBases()); + if (!nonVirtualBaseType) + accumulateVBases(); } llvm::stable_sort(members); @@ -275,7 +291,7 @@ void CIRRecordLowering::lower() { assert(!cir::MissingFeatures::recordZeroInit()); members.push_back(makeStorageInfo(size, getUIntNType(8))); - determinePacked(); + determinePacked(nonVirtualBaseType); insertPadding(); members.pop_back(); @@ -298,8 +314,9 @@ void CIRRecordLowering::fillOutputFields() { setBitFieldInfo(member.fieldDecl, member.offset, fieldTypes.back()); } else if (member.kind == MemberInfo::InfoKind::Base) { nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1; + } else if (member.kind == MemberInfo::InfoKind::VBase) { + virtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1; } - assert(!cir::MissingFeatures::recordLayoutVirtualBases()); } } @@ -426,8 +443,9 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field, limitOffset = bitsToCharUnits(getFieldBitOffset(*probe)); goto FoundLimit; } - assert(!cir::MissingFeatures::cxxSupport()); - limitOffset = astRecordLayout.getDataSize(); + limitOffset = cxxRecordDecl ? astRecordLayout.getNonVirtualSize() + : astRecordLayout.getDataSize(); + FoundLimit: CharUnits typeSize = getSize(type); if (beginOffset + typeSize <= limitOffset) { @@ -524,24 +542,25 @@ void CIRRecordLowering::calculateZeroInit() { continue; zeroInitializable = zeroInitializableAsBase = false; return; - } else if (member.kind == MemberInfo::InfoKind::Base) { + } else if (member.kind == MemberInfo::InfoKind::Base || + member.kind == MemberInfo::InfoKind::VBase) { if (isZeroInitializable(member.cxxRecordDecl)) continue; zeroInitializable = false; if (member.kind == MemberInfo::InfoKind::Base) zeroInitializableAsBase = false; } - assert(!cir::MissingFeatures::recordLayoutVirtualBases()); } } -void CIRRecordLowering::determinePacked() { +void CIRRecordLowering::determinePacked(bool nvBaseType) { if (packed) return; CharUnits alignment = CharUnits::One(); - - // TODO(cir): handle non-virtual base types - assert(!cir::MissingFeatures::cxxSupport()); + CharUnits nvAlignment = CharUnits::One(); + CharUnits nvSize = !nvBaseType && cxxRecordDecl + ? astRecordLayout.getNonVirtualSize() + : CharUnits::Zero(); for (const MemberInfo &member : members) { if (!member.data) @@ -550,12 +569,19 @@ void CIRRecordLowering::determinePacked() { // then the entire record must be packed. if (member.offset % getAlignment(member.data)) packed = true; + if (member.offset < nvSize) + nvAlignment = std::max(nvAlignment, getAlignment(member.data)); alignment = std::max(alignment, getAlignment(member.data)); } // If the size of the record (the capstone's offset) is not a multiple of the // record's alignment, it must be packed. if (members.back().offset % alignment) packed = true; + // If the non-virtual sub-object is not a multiple of the non-virtual + // sub-object's alignment, it must be packed. We cannot have a packed + // non-virtual sub-object and an unpacked complete object or vise versa. + if (nvSize % nvAlignment) + packed = true; // Update the alignment of the sentinel. if (!packed) members.back().data = getUIntNType(astContext.toBits(alignment)); @@ -589,7 +615,7 @@ std::unique_ptr CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { CIRRecordLowering lowering(*this, rd, /*packed=*/false); assert(ty->isIncomplete() && "recomputing record layout?"); - lowering.lower(); + lowering.lower(/*nonVirtualBaseType=*/false); // If we're in C++, compute the base subobject type. cir::RecordType baseTy; @@ -599,7 +625,7 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { if (lowering.astRecordLayout.getNonVirtualSize() != lowering.astRecordLayout.getSize()) { CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed); - baseLowering.lower(); + baseLowering.lower(/*NonVirtualBaseType=*/true); std::string baseIdentifier = getRecordTypeName(rd, ".base"); baseTy = builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier, @@ -626,8 +652,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { assert(!cir::MissingFeatures::recordZeroInit()); rl->nonVirtualBases.swap(lowering.nonVirtualBases); + rl->completeObjectVirtualBases.swap(lowering.virtualBases); - assert(!cir::MissingFeatures::cxxSupport()); assert(!cir::MissingFeatures::bitfields()); // Add all the field numbers. @@ -754,6 +780,17 @@ void CIRRecordLowering::lowerUnion() { packed = true; } +bool CIRRecordLowering::hasOwnStorage(const CXXRecordDecl *decl, + const CXXRecordDecl *query) { + const ASTRecordLayout &declLayout = astContext.getASTRecordLayout(decl); + if (declLayout.isPrimaryBaseVirtual() && declLayout.getPrimaryBase() == query) + return false; + for (const auto &base : decl->bases()) + if (!hasOwnStorage(base.getType()->getAsCXXRecordDecl(), query)) + return false; + return true; +} + /// The AAPCS that defines that, when possible, bit-fields should /// be accessed using containers of the declared type width: /// When a volatile bit-field is read, and its container does not overlap with @@ -873,7 +910,7 @@ void CIRRecordLowering::computeVolatileBitfields() { } } -void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) { +void CIRRecordLowering::accumulateBases() { // If we've got a primary virtual base, we need to add it with the bases. if (astRecordLayout.isPrimaryBaseVirtual()) { cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), @@ -881,12 +918,9 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) { } // Accumulate the non-virtual bases. - for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) { - if (base.isVirtual()) { - cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), - "accumulateBases: virtual base"); + for (const auto &base : cxxRecordDecl->bases()) { + if (base.isVirtual()) continue; - } // Bases can be zero-sized even if not technically empty if they // contain only a trailing array member. const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl(); @@ -899,6 +933,31 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) { } } +void CIRRecordLowering::accumulateVBases() { + for (const auto &base : cxxRecordDecl->vbases()) { + const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl(); + if (isEmptyRecordForLayout(astContext, base.getType())) + continue; + CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl); + // If the vbase is a primary virtual base of some base, then it doesn't + // get its own storage location but instead lives inside of that base. + if (isOverlappingVBaseABI() && astContext.isNearlyEmpty(baseDecl) && + !hasOwnStorage(cxxRecordDecl, baseDecl)) { + members.push_back( + MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, baseDecl)); + continue; + } + // If we've got a vtordisp, add it as a storage type. + if (astRecordLayout.getVBaseOffsetsMap() + .find(baseDecl) + ->second.hasVtorDisp()) + members.push_back(makeStorageInfo(offset - CharUnits::fromQuantity(4), + getUIntNType(32))); + members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, + getStorageType(baseDecl), baseDecl)); + } +} + void CIRRecordLowering::accumulateVPtrs() { if (astRecordLayout.hasOwnVFPtr()) members.push_back(MemberInfo(CharUnits::Zero(), MemberInfo::InfoKind::VFPtr, diff --git a/clang/test/CIR/CodeGen/vbase.cpp b/clang/test/CIR/CodeGen/vbase.cpp new file mode 100644 index 0000000000000..1d1b5e083bfc9 --- /dev/null +++ b/clang/test/CIR/CodeGen/vbase.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +struct A { + int a; +}; + +struct B: virtual A { + int b; +}; + +void ppp() { B b; } + +// Note: OGCG speculatively emits the VTT and VTables. This is not yet implemented in CIR. + +// Vtable definition for B +// CIR: cir.global "private" external @_ZTV1B + +// LLVM: @_ZTV1B = external global { [3 x ptr] } + +// OGCG: @_ZTV1B = linkonce_odr unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8 + +// Constructor for A +// CIR: cir.func comdat linkonce_odr @_ZN1AC2Ev(%arg0: !cir.ptr +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr +// CIR: cir.return + +// LLVM: define{{.*}} void @_ZN1AC2Ev(ptr %[[THIS_ARG:.*]]) { +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM: ret void + +// Note: OGCG elides the constructor for A. This is not yet implemented in CIR. + +// Constructor for B +// CIR: cir.func comdat linkonce_odr @_ZN1BC1Ev(%arg0: !cir.ptr +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr +// CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr nonnull [12] -> !cir.ptr +// CIR: cir.call @_ZN1AC2Ev(%[[BASE_A_ADDR]]) nothrow : (!cir.ptr) -> () +// CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = ) : !cir.vptr +// CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr -> !cir.ptr +// CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr +// CIR: cir.return + +// LLVM: define{{.*}} void @_ZN1BC1Ev(ptr %[[THIS_ARG:.*]]) { +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM: %[[BASE_A_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i32 12 +// LLVM: call void @_ZN1AC2Ev(ptr %[[BASE_A_ADDR]]) +// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 24), ptr %[[THIS]] +// LLVM: ret void + +// OGCG: define{{.*}} void @_ZN1BC1Ev(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ADDR:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// OGCG: %[[BASE_A_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 12 +// OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3), ptr %[[THIS]] +// OGCG: ret void + diff --git a/clang/test/CIR/CodeGen/vtt.cpp b/clang/test/CIR/CodeGen/vtt.cpp new file mode 100644 index 0000000000000..631aab428840a --- /dev/null +++ b/clang/test/CIR/CodeGen/vtt.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +// Note: This test will be expanded to verify VTT emission and VTT implicit +// argument handling. For now, it's just test the record layout. + +class A { +public: + int a; + virtual void v() {} +}; + +class B : public virtual A { +public: + int b; + virtual void w(); +}; + +class C : public virtual A { +public: + long c; + virtual void x() {} +}; + +class D : public B, public C { +public: + long d; + virtual void y() {} +}; + +// This is just here to force the record types to be emitted. +void f(D *d) {} + +// CIR: !rec_A2Ebase = !cir.record +// CIR: !rec_B2Ebase = !cir.record +// CIR: !rec_C2Ebase = !cir.record +// CIR: !rec_D = !cir.record + +// Nothing interesting to see here yet. +// LLVM: define{{.*}} void @_Z1fP1D +// OGCG: define{{.*}} void @_Z1fP1D