diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 3d120903dea19..a3ba40f73d06f 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -240,6 +240,7 @@ struct MissingFeatures { static bool builtinCall() { return false; } static bool builtinCallF128() { return false; } static bool builtinCallMathErrno() { return false; } + static bool ctorMemcpyizer() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index e59a1fdb837cb..278cc8931f308 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -53,19 +53,133 @@ bool CIRGenFunction::isConstructorDelegationValid( return true; } +static void emitLValueForAnyFieldInitialization(CIRGenFunction &cgf, + CXXCtorInitializer *memberInit, + LValue &lhs) { + FieldDecl *field = memberInit->getAnyMember(); + if (memberInit->isIndirectMemberInitializer()) { + // If we are initializing an anonymous union field, drill down to the field. + IndirectFieldDecl *indirectField = memberInit->getIndirectMember(); + for (const auto *nd : indirectField->chain()) { + auto *fd = cast(nd); + lhs = cgf.emitLValueForFieldInitialization(lhs, fd, fd->getName()); + } + } else { + lhs = cgf.emitLValueForFieldInitialization(lhs, field, field->getName()); + } +} + +static void emitMemberInitializer(CIRGenFunction &cgf, + const CXXRecordDecl *classDecl, + CXXCtorInitializer *memberInit, + const CXXConstructorDecl *constructor, + FunctionArgList &args) { + assert(memberInit->isAnyMemberInitializer() && + "Mush have member initializer!"); + assert(memberInit->getInit() && "Must have initializer!"); + + assert(!cir::MissingFeatures::generateDebugInfo()); + + // non-static data member initializers + FieldDecl *field = memberInit->getAnyMember(); + QualType fieldType = field->getType(); + + mlir::Value thisPtr = cgf.loadCXXThis(); + QualType recordTy = cgf.getContext().getTypeDeclType(classDecl); + + // If a base constructor is being emitted, create an LValue that has the + // non-virtual alignment. + LValue lhs = (cgf.curGD.getCtorType() == Ctor_Base) + ? cgf.makeNaturalAlignPointeeAddrLValue(thisPtr, recordTy) + : cgf.makeNaturalAlignAddrLValue(thisPtr, recordTy); + + emitLValueForAnyFieldInitialization(cgf, memberInit, lhs); + + // Special case: If we are in a copy or move constructor, and we are copying + // an array off PODs or classes with trivial copy constructors, ignore the AST + // and perform the copy we know is equivalent. + // FIXME: This is hacky at best... if we had a bit more explicit information + // in the AST, we could generalize it more easily. + const ConstantArrayType *array = + cgf.getContext().getAsConstantArrayType(fieldType); + if (array && constructor->isDefaulted() && + constructor->isCopyOrMoveConstructor()) { + QualType baseElementTy = cgf.getContext().getBaseElementType(array); + // NOTE(cir): CodeGen allows record types to be memcpy'd if applicable, + // whereas ClangIR wants to represent all object construction explicitly. + if (!baseElementTy->isRecordType()) { + cgf.cgm.errorNYI(memberInit->getSourceRange(), + "emitMemberInitializer: array of non-record type"); + return; + } + } + + cgf.emitInitializerForField(field, lhs, memberInit->getInit()); +} + /// This routine generates necessary code to initialize base classes and /// non-static data members belonging to this constructor. void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, CXXCtorType ctorType, FunctionArgList &args) { - if (cd->isDelegatingConstructor()) - return emitDelegatingCXXConstructorCall(cd, args); + if (cd->isDelegatingConstructor()) { + emitDelegatingCXXConstructorCall(cd, args); + return; + } + + // If there are no member initializers, we can just return. + if (cd->getNumCtorInitializers() == 0) + return; - if (cd->getNumCtorInitializers() != 0) { - // There's much more to do here. - cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer"); + 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: + // - This is a base ctor variant + // - There are no vbases + // - The class is abstract, so a complete object of it cannot be constructed + // + // The check for an abstract class is necessary because sema may not have + // marked virtual base destructors referenced. + bool constructVBases = ctorType != Ctor_Base && + classDecl->getNumVBases() != 0 && + !classDecl->isAbstract(); + if (constructVBases) { + cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base"); return; } + + if ((*b)->isBaseInitializer()) { + cgm.errorNYI(cd->getSourceRange(), + "emitCtorPrologue: non-virtual base initializer"); + return; + } + + if (classDecl->isDynamicClass()) { + cgm.errorNYI(cd->getSourceRange(), + "emitCtorPrologue: initialize vtable pointers"); + return; + } + + // Finally, initialize class members. + FieldConstructionScope fcs(*this, loadCXXThisAddress()); + // Classic codegen uses a special class to attempt to replace member + // initializers with memcpy. We could possibly defer that to the + // 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); + assert(!member->isBaseInitializer()); + assert(member->isAnyMemberInitializer() && + "Delegating initializer on non-delegating constructor"); + emitMemberInitializer(*this, cd->getParent(), member, cd, args); + } } Address CIRGenFunction::loadCXXThisAddress() { @@ -84,6 +198,33 @@ Address CIRGenFunction::loadCXXThisAddress() { return Address(loadCXXThis(), cxxThisAlignment); } +void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs, + Expr *init) { + QualType fieldType = field->getType(); + switch (getEvaluationKind(fieldType)) { + case cir::TEK_Scalar: + if (lhs.isSimple()) + emitExprAsInit(init, field, lhs, false); + else + cgm.errorNYI(field->getSourceRange(), + "emitInitializerForField: non-simple scalar"); + break; + case cir::TEK_Complex: + cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: complex"); + break; + case cir::TEK_Aggregate: { + cgm.errorNYI(field->getSourceRange(), "emitInitializerForField: aggregate"); + break; + } + } + + // Ensure that we destroy this object if an exception is thrown later in the + // constructor. + QualType::DestructionKind dtorKind = fieldType.isDestructedType(); + (void)dtorKind; + assert(!cir::MissingFeatures::requiresCleanups()); +} + void CIRGenFunction::emitDelegateCXXConstructorCall( const CXXConstructorDecl *ctor, CXXCtorType ctorType, const FunctionArgList &args, SourceLocation loc) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 4f2046ad26d72..95480dc8d4333 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -390,6 +390,34 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { return lv; } +LValue CIRGenFunction::emitLValueForFieldInitialization( + LValue base, const clang::FieldDecl *field, llvm::StringRef fieldName) { + QualType fieldType = field->getType(); + + if (!fieldType->isReferenceType()) + return emitLValueForField(base, field); + + const CIRGenRecordLayout &layout = + cgm.getTypes().getCIRGenRecordLayout(field->getParent()); + unsigned fieldIndex = layout.getCIRFieldNo(field); + + Address v = + emitAddrOfFieldStorage(base.getAddress(), field, fieldName, fieldIndex); + + // Make sure that the address is pointing to the right type. + mlir::Type memTy = convertTypeForMem(fieldType); + v = builder.createElementBitCast(getLoc(field->getSourceRange()), v, memTy); + + // TODO: Generate TBAA information that describes this access as a structure + // member access and not just an access to an object of the field's type. This + // should be similar to what we do in EmitLValueForField(). + LValueBaseInfo baseInfo = base.getBaseInfo(); + AlignmentSource fieldAlignSource = baseInfo.getAlignmentSource(); + LValueBaseInfo fieldBaseInfo(getFieldAlignmentSource(fieldAlignSource)); + assert(!cir::MissingFeatures::opTBAA()); + return makeAddrLValue(v, fieldType, fieldBaseInfo); +} + mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) { // Bool has a different representation in memory than in registers, // but in ClangIR, it is simply represented as a cir.bool value. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index fd413fe86383a..c029853929a58 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -550,6 +550,15 @@ LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val, return makeAddrLValue(Address(val, align), ty, baseInfo); } +LValue CIRGenFunction::makeNaturalAlignAddrLValue(mlir::Value val, + QualType ty) { + LValueBaseInfo baseInfo; + CharUnits alignment = cgm.getNaturalTypeAlignment(ty, &baseInfo); + Address addr(val, convertTypeForMem(ty), alignment); + assert(!cir::MissingFeatures::opTBAA()); + return makeAddrLValue(addr, ty, baseInfo); +} + clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, FunctionArgList &args) { const auto *fd = cast(gd.getDecl()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 6c490a72b2e93..93b9b504feb38 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -68,6 +68,10 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Value cxxThisValue = nullptr; clang::CharUnits cxxThisAlignment; + /// The value of 'this' to sue when evaluating CXXDefaultInitExprs within this + /// expression. + Address cxxDefaultInitExprThis = Address::invalid(); + // Holds the Decl for the current outermost non-closure context const clang::Decl *curFuncDecl = nullptr; @@ -490,7 +494,26 @@ class CIRGenFunction : public CIRGenTypeCache { static bool isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor); + /// A scope within which we are constructing the fields of an object which + /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if + /// we need to evaluate the CXXDefaultInitExpr within the evaluation. + class FieldConstructionScope { + public: + FieldConstructionScope(CIRGenFunction &cgf, Address thisAddr) + : cgf(cgf), oldCXXDefaultInitExprThis(cgf.cxxDefaultInitExprThis) { + cgf.cxxDefaultInitExprThis = thisAddr; + } + ~FieldConstructionScope() { + cgf.cxxDefaultInitExprThis = oldCXXDefaultInitExprThis; + } + + private: + CIRGenFunction &cgf; + Address oldCXXDefaultInitExprThis; + }; + LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t); + LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty); /// Construct an address with the natural alignment of T. If a pointer to T /// is expected to be signed, the pointer passed to this function must have @@ -844,6 +867,9 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitFunctionBody(const clang::Stmt *body); + void emitInitializerForField(clang::FieldDecl *field, LValue lhs, + clang::Expr *init); + mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType); /// Emit the computation of the specified expression of scalar type. @@ -938,6 +964,13 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitLValue(const clang::Expr *e); LValue emitLValueForField(LValue base, const clang::FieldDecl *field); + /// Like emitLValueForField, excpet that if the Field is a reference, this + /// will return the address of the reference and not the address of the value + /// stored in the reference. + LValue emitLValueForFieldInitialization(LValue base, + const clang::FieldDecl *field, + llvm::StringRef fieldName); + LValue emitMemberExpr(const MemberExpr *e); /// Given an expression with a pointer type, emit the value and compute our diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 434dd376208e1..68ab81ed53af9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -68,6 +68,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, // Initialize cached types VoidTy = cir::VoidType::get(&getMLIRContext()); + VoidPtrTy = cir::PointerType::get(VoidTy); SInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/true); SInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/true); SInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/true); @@ -94,6 +95,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed const unsigned sizeTypeSize = astContext.getTypeSize(astContext.getSignedSizeType()); + // In CIRGenTypeCache, UIntPtrTy and SizeType are fields of the same union + UIntPtrTy = + cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/false); PtrDiffTy = cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/true); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h index a5b7f0c9579b4..12dbc3297a072 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -22,7 +22,7 @@ namespace clang::CIRGen { /// during IR emission. It's initialized once in CodeGenModule's /// constructor and then copied around into new CIRGenFunction's. struct CIRGenTypeCache { - CIRGenTypeCache() = default; + CIRGenTypeCache() {} // ClangIR void type cir::VoidType VoidTy; @@ -49,8 +49,17 @@ struct CIRGenTypeCache { cir::FP80Type FP80Ty; cir::FP128Type FP128Ty; + /// intptr_t, size_t, and ptrdiff_t, which we assume are the same size. + union { + mlir::Type UIntPtrTy; + mlir::Type SizeTy; + }; + mlir::Type PtrDiffTy; + /// void* in address space 0 + cir::PointerType VoidPtrTy; + /// The size and alignment of a pointer into the generic address space. union { unsigned char PointerAlignInBytes; diff --git a/clang/test/CIR/CodeGen/ctor.cpp b/clang/test/CIR/CodeGen/ctor.cpp index 1a36eb0d9d3a6..0b009442b2f87 100644 --- a/clang/test/CIR/CodeGen/ctor.cpp +++ b/clang/test/CIR/CodeGen/ctor.cpp @@ -113,3 +113,109 @@ void bam() { // CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init] // CHECK-NEXT: cir.call @_ZN15DelegatingStrukC1Ev(%[[S_ADDR]]) // CHECK-NEXT: cir.return + +struct MemberInitStruk { + int a; + MemberInitStruk() : a(0) {} +}; + +void init_member() { + MemberInitStruk s; +} + +// CHECK: cir.func @_ZN15MemberInitStrukC2Ev(%arg0: !cir.ptr +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CHECK-NEXT: %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"} +// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CHECK-NEXT: cir.store align(4) %[[ZERO]], %[[A_ADDR]] +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_ZN15MemberInitStrukC1Ev(%arg0: !cir.ptr +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CHECK-NEXT: cir.call @_ZN15MemberInitStrukC2Ev(%[[THIS]]) +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_Z11init_memberv +// CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init] +// CHECK-NEXT: cir.call @_ZN15MemberInitStrukC1Ev(%[[S_ADDR]]) +// CHECK-NEXT: cir.return + +struct ParamMemberInitStruk { + int a; + ParamMemberInitStruk(int n) : a(n) {} +}; + +void init_param_member() { + ParamMemberInitStruk s(0); +} + +// CHECK: cir.func @_ZN20ParamMemberInitStrukC2Ei(%arg0: !cir.ptr +// CHECK-SAME: %arg1: !s32i +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: cir.store %arg1, %[[N_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CHECK-NEXT: %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"} +// CHECK-NEXT: %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]] +// CHECK-NEXT: cir.store{{.*}} %[[N]], %[[A_ADDR]] +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_ZN20ParamMemberInitStrukC1Ei(%arg0: !cir.ptr +// CHECK-SAME: %arg1: !s32i +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: %[[N_ADDR:.*]] = cir.alloca {{.*}} ["n", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: cir.store %arg1, %[[N_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CHECK-NEXT: %[[N:.*]] = cir.load{{.*}} %[[N_ADDR]] +// CHECK-NEXT: cir.call @_ZN20ParamMemberInitStrukC2Ei(%[[THIS]], %[[N]]) +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_Z17init_param_memberv +// CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init] +// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> +// CHECK-NEXT: cir.call @_ZN20ParamMemberInitStrukC1Ei(%[[S_ADDR]], %[[ZERO]]) +// CHECK-NEXT: cir.return + +struct UnionInitStruk { + union { + int a; + union { + float b; + double c; + }; + }; + UnionInitStruk() : c(0.0) {} +}; + +void init_union() { + UnionInitStruk s; +} + +// CHECK: cir.func @_ZN14UnionInitStrukC2Ev(%arg0: !cir.ptr +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CHECK-NEXT: %[[AU1_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = ""} +// CHECK-NEXT: %[[AU2_ADDR:.*]] = cir.get_member %[[AU1_ADDR]][1] {name = ""} +// CHECK-NEXT: %[[C_ADDR:.*]] = cir.get_member %[[AU2_ADDR]][1] {name = "c"} +// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0.000000e+00> +// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[C_ADDR]] +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_ZN14UnionInitStrukC1Ev(%arg0: !cir.ptr +// CHECK-NEXT: %[[THIS_ADDR:.*]] = cir.alloca {{.*}} ["this", init] +// CHECK-NEXT: cir.store %arg0, %[[THIS_ADDR]] +// CHECK-NEXT: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CHECK-NEXT: cir.call @_ZN14UnionInitStrukC2Ev +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_Z10init_unionv +// CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init] +// CHECK-NEXT: cir.call @_ZN14UnionInitStrukC1Ev(%[[S_ADDR]]) +// CHECK-NEXT: cir.return