Skip to content

Commit

Permalink
[CIR][CIRGen] Implement delegating constructors (#821)
Browse files Browse the repository at this point in the history
This is a straightforward adaption from CodeGen. I checked the uses of
the Delegating arg that's passed in various places, and it only appears
to be used by virtual inheritance, which should be handled by
#624.
  • Loading branch information
smeenai committed Sep 9, 2024
1 parent 97357e1 commit c329de7
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 4 deletions.
51 changes: 48 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ bool CIRGenFunction::IsConstructorDelegationValid(

// FIXME: Decide if we can do a delegation of a delegating constructor.
if (Ctor->isDelegatingConstructor())
llvm_unreachable("NYI");
return false;

return true;
}
Expand Down Expand Up @@ -585,7 +585,7 @@ void CIRGenFunction::buildCtorPrologue(const CXXConstructorDecl *CD,
CXXCtorType CtorType,
FunctionArgList &Args) {
if (CD->isDelegatingConstructor())
llvm_unreachable("NYI");
return buildDelegatingCXXConstructorCall(CD, Args);

const CXXRecordDecl *ClassDecl = CD->getParent();

Expand Down Expand Up @@ -1379,6 +1379,51 @@ void CIRGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD,
assert(!MissingFeatures::sanitizeDtor());
}

namespace {
struct CallDelegatingCtorDtor final : EHScopeStack::Cleanup {
const CXXDestructorDecl *Dtor;
Address Addr;
CXXDtorType Type;

CallDelegatingCtorDtor(const CXXDestructorDecl *D, Address Addr,
CXXDtorType Type)
: Dtor(D), Addr(Addr), Type(Type) {}

void Emit(CIRGenFunction &CGF, Flags flags) override {
// We are calling the destructor from within the constructor.
// Therefore, "this" should have the expected type.
QualType ThisTy = Dtor->getFunctionObjectParameterType();
CGF.buildCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false,
/*Delegating=*/true, Addr, ThisTy);
}
};
} // end anonymous namespace

void CIRGenFunction::buildDelegatingCXXConstructorCall(
const CXXConstructorDecl *Ctor, const FunctionArgList &Args) {
assert(Ctor->isDelegatingConstructor());

Address ThisPtr = LoadCXXThisAddress();

AggValueSlot AggSlot = AggValueSlot::forAddr(
ThisPtr, Qualifiers(), AggValueSlot::IsDestructed,
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsNotAliased,
AggValueSlot::MayOverlap, AggValueSlot::IsNotZeroed,
// Checks are made by the code that calls constructor.
AggValueSlot::IsSanitizerChecked);

buildAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot);

const CXXRecordDecl *ClassDecl = Ctor->getParent();
if (CGM.getLangOpts().Exceptions && !ClassDecl->hasTrivialDestructor()) {
CXXDtorType Type =
CurGD.getCtorType() == Ctor_Complete ? Dtor_Complete : Dtor_Base;

EHStack.pushCleanup<CallDelegatingCtorDtor>(
EHCleanup, ClassDecl->getDestructor(), ThisPtr, Type);
}
}

void CIRGenFunction::buildCXXDestructorCall(const CXXDestructorDecl *DD,
CXXDtorType Type,
bool ForVirtualBase,
Expand Down Expand Up @@ -1710,4 +1755,4 @@ void CIRGenFunction::buildCXXAggrConstructorCall(

if (constantCount.use_empty())
constantCount.erase();
}
}
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,9 @@ void CIRGenFunction::buildCXXConstructExpr(const CXXConstructExpr *E,
Type = Ctor_Complete;
break;
case CXXConstructionKind::Delegating:
llvm_unreachable("NYI");
// We should be emitting a constructor; GlobalDecl will assert this
Type = CurGD.getCtorType();
Delegating = true;
break;
case CXXConstructionKind::VirtualBase:
ForVirtualBase = true;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,13 @@ class CIRGenFunction : public CIRGenTypeCache {
const FunctionArgList &Args,
clang::SourceLocation Loc);

// It's important not to confuse this and the previous function. 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 buildDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor,
const FunctionArgList &Args);

/// We are performing a delegate call; that is, the current function is
/// delegating to another one. Produce a r-value suitable for passing the
/// given parameter.
Expand Down
88 changes: 88 additions & 0 deletions clang/test/CIR/CodeGen/delegating-ctor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -fexceptions -fcxx-exceptions %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

struct Delegating {
Delegating();
Delegating(int);
};

// Check that the constructor being delegated to is called with the correct
// arguments.
Delegating::Delegating() : Delegating(0) {}

// CHECK-LABEL: cir.func @_ZN10DelegatingC2Ev(%arg0: !cir.ptr<!ty_22Delegating22> {{.*}}) {{.*}} {
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22Delegating22>, !cir.ptr<!cir.ptr<!ty_22Delegating22>>, ["this", init] {alignment = 8 : i64}
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22Delegating22>, !cir.ptr<!cir.ptr<!ty_22Delegating22>>
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22Delegating22>>, !cir.ptr<!ty_22Delegating22>
// CHECK-NEXT: %2 = cir.const #cir.int<0> : !s32i
// CHECK-NEXT: cir.call @_ZN10DelegatingC2Ei(%1, %2) : (!cir.ptr<!ty_22Delegating22>, !s32i) -> ()
// CHECK-NEXT: cir.return
// CHECK-NEXT: }

struct DelegatingWithZeroing {
int i;
DelegatingWithZeroing() = default;
DelegatingWithZeroing(int);
};

// Check that the delegating constructor performs zero-initialization here.
// FIXME: we should either emit the trivial default constructor or remove the
// call to it in a lowering pass.
DelegatingWithZeroing::DelegatingWithZeroing(int) : DelegatingWithZeroing() {}

// CHECK-LABEL: cir.func @_ZN21DelegatingWithZeroingC2Ei(%arg0: !cir.ptr<!ty_22DelegatingWithZeroing22> {{.*}}, %arg1: !s32i {{.*}}) {{.*}} {
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22DelegatingWithZeroing22>, !cir.ptr<!cir.ptr<!ty_22DelegatingWithZeroing22>>, ["this", init] {alignment = 8 : i64}
// CHECK-NEXT: %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["", init] {alignment = 4 : i64}
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22DelegatingWithZeroing22>, !cir.ptr<!cir.ptr<!ty_22DelegatingWithZeroing22>>
// CHECK-NEXT: cir.store %arg1, %1 : !s32i, !cir.ptr<!s32i>
// CHECK-NEXT: %2 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22DelegatingWithZeroing22>>, !cir.ptr<!ty_22DelegatingWithZeroing22>
// CHECK-NEXT: %3 = cir.const #cir.zero : !ty_22DelegatingWithZeroing22
// CHECK-NEXT: cir.store %3, %2 : !ty_22DelegatingWithZeroing22, !cir.ptr<!ty_22DelegatingWithZeroing22>
// CHECK-NEXT: cir.call @_ZN21DelegatingWithZeroingC2Ev(%2) : (!cir.ptr<!ty_22DelegatingWithZeroing22>) -> () extra(#fn_attr1)
// CHECK-NEXT: cir.return
// CHECK-NEXT: }

void canThrow();
struct HasNonTrivialDestructor {
HasNonTrivialDestructor();
HasNonTrivialDestructor(int);
~HasNonTrivialDestructor();
};

// Check that we call the destructor whenever a cleanup is needed.
// FIXME: enable and check this when exceptions are fully supported.
#if 0
HasNonTrivialDestructor::HasNonTrivialDestructor(int)
: HasNonTrivialDestructor() {
canThrow();
}
#endif

// From clang/test/CodeGenCXX/cxx0x-delegating-ctors.cpp, check that virtual
// inheritance and delegating constructors interact correctly.
// FIXME: enable and check this when virtual inheritance is fully supported.
#if 0
namespace PR14588 {
void other();

class Base {
public:
Base() { squawk(); }
virtual ~Base() {}

virtual void squawk() { other(); }
};

class Foo : public virtual Base {
public:
Foo();
Foo(const void *inVoid);
virtual ~Foo() {}

virtual void squawk() { other(); }
};

Foo::Foo() : Foo(nullptr) { other(); }
Foo::Foo(const void *inVoid) { squawk(); }
} // namespace PR14588
#endif

0 comments on commit c329de7

Please sign in to comment.