https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/138368
This change adds additional checks to a few places where a simple struct in C++ code was triggering `errorNYI` in places where no additional handling was needed, and adds a very small amount of trivial initialization. The code now checks for the conditions that do require extra handling before issuing the diagnostic. New tests are added for declaring and using a simple struct in C++ code. >From 483efe53c40d12895dc264fa993d3b286f8d85b2 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Tue, 29 Apr 2025 12:59:13 -0700 Subject: [PATCH] [CIR] Unblock simple C++ structure support This change adds additional checks to a few places where a simple struct in C++ code was triggering `errorNYI` in places where no additional handling was needed, and adds a very small amount of trivial initialization. The code now checks for the conditions that do require extra handling before issuing the diagnostic. New tests are added for declaring and using a simple struct in C++ code. --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 9 +++-- clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 13 +++++-- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 25 +++++++++---- clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 7 +++- clang/test/CIR/CodeGen/struct.cpp | 37 +++++++++++++++++++ 5 files changed, 75 insertions(+), 16 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 94a6c03f7f1a4..64cbda2ebe0af 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -322,9 +322,12 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { assert(!cir::MissingFeatures::opTBAA()); Address addr = base.getAddress(); - if (isa<CXXRecordDecl>(rec)) { - cgm.errorNYI(field->getSourceRange(), "emitLValueForField: C++ class"); - return LValue(); + if (auto *classDecl = dyn_cast<CXXRecordDecl>(rec)) { + if (cgm.getCodeGenOpts().StrictVTablePointers && + classDecl->isDynamicClass()) { + cgm.errorNYI(field->getSourceRange(), + "emitLValueForField: strict vtable for dynamic class"); + } } unsigned recordCVR = base.getVRQualifiers(); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index ab1ea07bbf5ef..9e1e2e4dd6b58 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -365,10 +365,15 @@ mlir::Attribute ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &d) { if (!d.hasLocalStorage()) { QualType ty = cgm.getASTContext().getBaseElementType(d.getType()); if (ty->isRecordType()) - if (d.getInit() && isa<CXXConstructExpr>(d.getInit())) { - cgm.errorNYI(d.getInit()->getBeginLoc(), - "tryEmitPrivateForVarInit CXXConstructExpr"); - return {}; + if (const CXXConstructExpr *e = + dyn_cast_or_null<CXXConstructExpr>(d.getInit())) { + const CXXConstructorDecl *cd = e->getConstructor(); + // FIXME: we should probably model this more closely to C++ than + // just emitting a global with zero init (mimic what we do for trivial + // assignments and whatnots). Since this is for globals shouldn't + // be a problem for the near future. + if (cd->isTrivial() && cd->isDefaultConstructor()) + return cir::ZeroAttr::get(cgm.convertType(d.getType())); } } inConstantContext = d.hasConstantInitialization(); diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 5bcd408b4072a..2b95d2e12014c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -177,18 +177,26 @@ void CIRRecordLowering::lower() { return; } - if (isa<CXXRecordDecl>(recordDecl)) { - cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), - "lower: class"); - return; - } - assert(!cir::MissingFeatures::cxxSupport()); CharUnits size = astRecordLayout.getSize(); accumulateFields(); + if (auto const *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl)) { + if (cxxRecordDecl->getNumBases() > 0) { + CIRGenModule &cgm = cirGenTypes.getCGModule(); + cgm.errorNYI(recordDecl->getSourceRange(), + "CIRRecordLowering::lower: derived CXXRecordDecl"); + return; + } + if (members.empty()) { + appendPaddingBytes(size); + assert(!cir::MissingFeatures::bitfields()); + return; + } + } + llvm::stable_sort(members); // TODO: implement clipTailPadding once bitfields are implemented assert(!cir::MissingFeatures::bitfields()); @@ -295,7 +303,10 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { // If we're in C++, compute the base subobject type. if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() && !rd->hasAttr<FinalAttr>()) { - cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: CXXRecordDecl"); + if (lowering.astRecordLayout.getNonVirtualSize() != + lowering.astRecordLayout.getSize()) { + cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: CXXRecordDecl"); + } } // Fill in the record *after* computing the base type. Filling in the body diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index e85f2f4aa0978..ef17d622f1d27 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -237,8 +237,11 @@ mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) { assert(insertResult && "isSafeToCovert() should have caught this."); // Force conversion of non-virtual base classes recursively. - if (isa<CXXRecordDecl>(rd)) { - cgm.errorNYI(rd->getSourceRange(), "CXXRecordDecl"); + if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(rd)) { + if (cxxRecordDecl->getNumBases() > 0) { + cgm.errorNYI(rd->getSourceRange(), + "convertRecordDeclType: derived CXXRecordDecl"); + } } // Layout fields. diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp index 0d939ddd0b338..208d8f184475c 100644 --- a/clang/test/CIR/CodeGen/struct.cpp +++ b/clang/test/CIR/CodeGen/struct.cpp @@ -12,6 +12,17 @@ IncompleteS *p; // LLVM: @p = dso_local global ptr null // OGCG: @p = global ptr null, align 8 +struct CompleteS { + int a; + char b; +}; + +CompleteS cs; + +// CIR: cir.global external @cs = #cir.zero : !rec_CompleteS +// LLVM-DAG: @cs = dso_local global %struct.CompleteS zeroinitializer +// OGCG-DAG: @cs = global %struct.CompleteS zeroinitializer, align 4 + void f(void) { IncompleteS *p; } @@ -28,3 +39,29 @@ void f(void) { // OGCG-NEXT: entry: // OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 // OGCG-NEXT: ret void + +char f2(CompleteS &s) { + return s.b; +} + +// CIR: cir.func @_Z2f2R9CompleteS(%[[ARG_S:.*]]: !cir.ptr<!rec_CompleteS>{{.*}}) +// CIR: %[[S_ADDR:.*]] = cir.alloca !cir.ptr<!rec_CompleteS>, !cir.ptr<!cir.ptr<!rec_CompleteS>>, ["s", init, const] +// CIR: cir.store %[[ARG_S]], %[[S_ADDR]] +// CIR: %[[S_REF:.*]] = cir.load %[[S_ADDR]] +// CIR: %[[S_ADDR2:.*]] = cir.get_member %[[S_REF]][1] {name = "b"} +// CIR: %[[S_B:.*]] = cir.load %[[S_ADDR2]] + +// LLVM: define i8 @_Z2f2R9CompleteS(ptr %[[ARG_S:.*]]) +// LLVM: %[[S_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[ARG_S]], ptr %[[S_ADDR]] +// LLVM: %[[S_REF:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// LLVM: %[[S_ADDR2:.*]] = getelementptr %struct.CompleteS, ptr %[[S_REF]], i32 0, i32 1 +// LLVM: %[[S_B:.*]] = load i8, ptr %[[S_ADDR2]] + +// OGCG: define{{.*}} i8 @_Z2f2R9CompleteS(ptr{{.*}} %[[ARG_S:.*]]) +// OGCG: entry: +// OGCG: %[[S_ADDR:.*]] = alloca ptr +// OGCG: store ptr %[[ARG_S]], ptr %[[S_ADDR]] +// OGCG: %[[S_REF:.*]] = load ptr, ptr %[[S_ADDR]] +// OGCG: %[[S_ADDR2:.*]] = getelementptr inbounds nuw %struct.CompleteS, ptr %[[S_REF]], i32 0, i32 1 +// OGCG: %[[S_B:.*]] = load i8, ptr %[[S_ADDR2]] _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits