diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index fbd15d5c886d2..97b933657d742 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -172,6 +172,7 @@ struct MissingFeatures { static bool astVarDeclInterface() { return false; } static bool stackSaveOp() { return false; } static bool aggValueSlot() { return false; } + static bool aggValueSlotMayOverlap() { return false; } static bool generateDebugInfo() { return false; } static bool pointerOverflowSanitizer() { return false; } static bool fpConstraints() { return false; } @@ -227,7 +228,6 @@ struct MissingFeatures { static bool implicitConstructorArgs() { return false; } static bool intrinsics() { return false; } static bool attributeNoBuiltin() { return false; } - static bool emitCtorPrologue() { return false; } static bool thunks() { return false; } static bool runCleanupsScope() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index da754e0806b2d..fa867ab12313c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -189,8 +189,7 @@ void CIRGenFunction::emitDelegateCallArg(CallArgList &args, // For the most part, we just need to load the alloca, except that aggregate // r-values are actually pointers to temporaries. } else { - cgm.errorNYI(param->getSourceRange(), - "emitDelegateCallArg: convertTempToRValue"); + args.add(convertTempToRValue(local, type, loc), type); } // Deactivate the cleanup for the callee-destructed param that was pushed. diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index bb4b451c99247..e59a1fdb837cb 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -53,6 +53,21 @@ bool CIRGenFunction::isConstructorDelegationValid( return true; } +/// 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->getNumCtorInitializers() != 0) { + // There's much more to do here. + cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: any initializer"); + return; + } +} + Address CIRGenFunction::loadCXXThisAddress() { assert(curFuncDecl && "loading 'this' without a func declaration?"); assert(isa(curFuncDecl)); @@ -102,6 +117,29 @@ void CIRGenFunction::emitDelegateCXXConstructorCall( /*Delegating=*/true, thisAddr, delegateArgs, loc); } +void CIRGenFunction::emitDelegatingCXXConstructorCall( + const CXXConstructorDecl *ctor, const FunctionArgList &args) { + assert(ctor->isDelegatingConstructor()); + + Address thisPtr = loadCXXThisAddress(); + + assert(!cir::MissingFeatures::objCGC()); + assert(!cir::MissingFeatures::sanitizers()); + AggValueSlot aggSlot = AggValueSlot::forAddr( + thisPtr, Qualifiers(), AggValueSlot::IsDestructed, + AggValueSlot::IsNotAliased, AggValueSlot::MayOverlap, + AggValueSlot::IsNotZeroed); + + emitAggExpr(ctor->init_begin()[0]->getInit(), aggSlot); + + const CXXRecordDecl *classDecl = ctor->getParent(); + if (cgm.getLangOpts().Exceptions && !classDecl->hasTrivialDestructor()) { + cgm.errorNYI(ctor->getSourceRange(), + "emitDelegatingCXXConstructorCall: exception"); + return; + } +} + Address CIRGenFunction::getAddressOfBaseClass( Address value, const CXXRecordDecl *derived, llvm::iterator_range path, diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 80b0172090aa3..748c2b5f6fceb 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -259,7 +259,12 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, return; } case cir::TEK_Aggregate: - emitAggExpr(init, AggValueSlot::forLValue(lvalue)); + // The overlap flag here should be calculated. + assert(!cir::MissingFeatures::aggValueSlotMayOverlap()); + emitAggExpr(init, + AggValueSlot::forLValue(lvalue, AggValueSlot::IsDestructed, + AggValueSlot::IsNotAliased, + AggValueSlot::MayOverlap)); return; } llvm_unreachable("bad evaluation kind"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index f1f86509c9a9b..5d04faf443b8d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1261,6 +1261,23 @@ Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) { return Address(ptr, addr.getAlignment()); } +/// Given the address of a temporary variable, produce an r-value of its type. +RValue CIRGenFunction::convertTempToRValue(Address addr, clang::QualType type, + clang::SourceLocation loc) { + LValue lvalue = makeAddrLValue(addr, type, AlignmentSource::Decl); + switch (getEvaluationKind(type)) { + case cir::TEK_Complex: + cgm.errorNYI(loc, "convertTempToRValue: complex type"); + return RValue::get(nullptr); + case cir::TEK_Aggregate: + cgm.errorNYI(loc, "convertTempToRValue: aggregate type"); + return RValue::get(nullptr); + case cir::TEK_Scalar: + return RValue::get(emitLoadOfScalar(lvalue, loc)); + } + llvm_unreachable("bad evaluation kind"); +} + /// Emit an `if` on a boolean condition, filling `then` and `else` into /// appropriated regions. mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond, @@ -1473,6 +1490,10 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e, type = Ctor_Complete; break; case CXXConstructionKind::Delegating: + // We should be emitting a constructor; GlobalDecl will assert this + type = curGD.getCtorType(); + delegating = true; + break; case CXXConstructionKind::VirtualBase: case CXXConstructionKind::NonVirtualBase: cgm.errorNYI(e->getSourceRange(), diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index f1df1b79fc48e..061123d55b882 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -203,7 +203,11 @@ void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) { cgf.cgm.errorNYI("emitInitializationToLValue TEK_Complex"); break; case cir::TEK_Aggregate: - cgf.emitAggExpr(e, AggValueSlot::forLValue(lv)); + cgf.emitAggExpr(e, AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed, + AggValueSlot::IsNotAliased, + AggValueSlot::MayOverlap, + dest.isZeroed())); + return; case cir::TEK_Scalar: if (lv.isSimple()) @@ -284,6 +288,8 @@ LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) { assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!"); Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange())); LValue lv = makeAddrLValue(temp, e->getType()); - emitAggExpr(e, AggValueSlot::forLValue(lv)); + emitAggExpr(e, AggValueSlot::forLValue(lv, AggValueSlot::IsNotDestructed, + AggValueSlot::IsNotAliased, + AggValueSlot::DoesNotOverlap)); return lv; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 53c44c6cc7680..c5bd5109343d3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -526,14 +526,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { // TODO: in restricted cases, we can emit the vbase initializers of a // complete ctor and then delegate to the base ctor. - assert(!cir::MissingFeatures::emitCtorPrologue()); - if (ctor->isDelegatingConstructor()) { - // This will be handled in emitCtorPrologue, but we should emit a diagnostic - // rather than silently fail to delegate. - cgm.errorNYI(ctor->getSourceRange(), - "emitConstructorBody: delegating ctor"); - return; - } + // Emit the constructor prologue, i.e. the base and member initializers. + emitCtorPrologue(ctor, ctorType, args); // TODO(cir): propagate this result via mlir::logical result. Just unreachable // now just to have it handled. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 361dcd5ef1c31..cf672b0c90e60 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -474,6 +474,9 @@ class CIRGenFunction : public CIRGenTypeCache { bool shouldNullCheckClassCastValue(const CastExpr *ce); + RValue convertTempToRValue(Address addr, clang::QualType type, + clang::SourceLocation loc); + static bool isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor); @@ -797,6 +800,16 @@ class CIRGenFunction : public CIRGenTypeCache { const CXXMethodDecl *md, ReturnValueSlot returnValue); + void emitCtorPrologue(const clang::CXXConstructorDecl *ctor, + clang::CXXCtorType ctorType, FunctionArgList &args); + + // It's important not to confuse this and emitDelegateCXXConstructorCall. + // Delegating constructors are the C++11 feature. The constructor delegate + // optimization is used to reduce duplication in the base and complete + // constructors where they are substantially the same. + void emitDelegatingCXXConstructorCall(const CXXConstructorDecl *ctor, + const FunctionArgList &args); + mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); /// Emit an expression as an initializer for an object (variable, field, etc.) diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 208247e16e531..8f52fea31750c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -267,23 +267,64 @@ class AggValueSlot { Address addr; clang::Qualifiers quals; + /// This is set to true if some external code is responsible for setting up a + /// destructor for the slot. Otherwise the code which constructs it should + /// push the appropriate cleanup. + LLVM_PREFERRED_TYPE(bool) + [[maybe_unused]] unsigned destructedFlag : 1; + /// This is set to true if the memory in the slot is known to be zero before /// the assignment into it. This means that zero fields don't need to be set. - bool zeroedFlag : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned zeroedFlag : 1; + + /// This is set to true if the slot might be aliased and it's not undefined + /// behavior to access it through such an alias. Note that it's always + /// undefined behavior to access a C++ object that's under construction + /// through an alias derived from outside the construction process. + /// + /// This flag controls whether calls that produce the aggregate + /// value may be evaluated directly into the slot, or whether they + /// must be evaluated into an unaliased temporary and then memcpy'ed + /// over. Since it's invalid in general to memcpy a non-POD C++ + /// object, it's important that this flag never be set when + /// evaluating an expression which constructs such an object. + LLVM_PREFERRED_TYPE(bool) + [[maybe_unused]] unsigned aliasedFlag : 1; + + /// This is set to true if the tail padding of this slot might overlap + /// another object that may have already been initialized (and whose + /// value must be preserved by this initialization). If so, we may only + /// store up to the dsize of the type. Otherwise we can widen stores to + /// the size of the type. + LLVM_PREFERRED_TYPE(bool) + [[maybe_unused]] unsigned overlapFlag : 1; public: + enum IsDestructed_t { IsNotDestructed, IsDestructed }; enum IsZeroed_t { IsNotZeroed, IsZeroed }; + enum IsAliased_t { IsNotAliased, IsAliased }; + enum Overlap_t { MayOverlap, DoesNotOverlap }; - AggValueSlot(Address addr, clang::Qualifiers quals, bool zeroedFlag) - : addr(addr), quals(quals), zeroedFlag(zeroedFlag) {} + AggValueSlot(Address addr, clang::Qualifiers quals, bool destructedFlag, + bool zeroedFlag, bool aliasedFlag, bool overlapFlag) + : addr(addr), quals(quals), destructedFlag(destructedFlag), + zeroedFlag(zeroedFlag), aliasedFlag(aliasedFlag), + overlapFlag(overlapFlag) {} static AggValueSlot forAddr(Address addr, clang::Qualifiers quals, + IsDestructed_t isDestructed, + IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed) { - return AggValueSlot(addr, quals, isZeroed); + return AggValueSlot(addr, quals, isDestructed, isZeroed, isAliased, + mayOverlap); } - static AggValueSlot forLValue(const LValue &lv) { - return forAddr(lv.getAddress(), lv.getQuals()); + static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed, + IsAliased_t isAliased, Overlap_t mayOverlap, + IsZeroed_t isZeroed = IsNotZeroed) { + return forAddr(LV.getAddress(), LV.getQuals(), isDestructed, isAliased, + mayOverlap, isZeroed); } clang::Qualifiers getQualifiers() const { return quals; } diff --git a/clang/test/CIR/CodeGen/ctor.cpp b/clang/test/CIR/CodeGen/ctor.cpp index 3b4191fd74c97..1a36eb0d9d3a6 100644 --- a/clang/test/CIR/CodeGen/ctor.cpp +++ b/clang/test/CIR/CodeGen/ctor.cpp @@ -67,3 +67,49 @@ void bar() { // CHECK-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i // CHECK-NEXT: cir.call @_ZN13VariadicStrukC1Eiz(%[[S_ADDR]], %[[ONE]], %[[TWO]], %[[THREE]]) // CHECK-NEXT: cir.return + +struct DelegatingStruk { + int a; + DelegatingStruk(int n) { a = n; } + DelegatingStruk() : DelegatingStruk(0) {} +}; + +void bam() { + DelegatingStruk s; +} + +// CHECK: cir.func @_ZN15DelegatingStrukC2Ei(%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: %[[A_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "a"} +// CHECK-NEXT: cir.store{{.*}} %[[N]], %[[A_ADDR]] +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_ZN15DelegatingStrukC1Ei(%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 @_ZN15DelegatingStrukC2Ei(%[[THIS]], %[[N]]) +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_ZN15DelegatingStrukC1Ev(%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: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CHECK-NEXT: cir.call @_ZN15DelegatingStrukC1Ei(%[[THIS]], %[[ZERO]]) +// CHECK-NEXT: cir.return + +// CHECK: cir.func @_Z3bamv +// CHECK-NEXT: %[[S_ADDR:.*]] = cir.alloca {{.*}} ["s", init] +// CHECK-NEXT: cir.call @_ZN15DelegatingStrukC1Ev(%[[S_ADDR]]) +// CHECK-NEXT: cir.return