Skip to content

Commit 3933c43

Browse files
committed
[clang] Add cc1 option -fctor-dtor-return-this
This option forces constructors and non-deleting destructors to return `this` pointer in C++ ABI (except for Microsoft ABI, on which this flag has no effect). This is similar to ARM32, Apple ARM64, or Fuchsia C++ ABI, but can be applied to any target triple. Differential Revision: https://reviews.llvm.org/D119209
1 parent 4247cdb commit 3933c43

File tree

5 files changed

+64
-35
lines changed

5 files changed

+64
-35
lines changed

clang/include/clang/Basic/CodeGenOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,10 @@ ENUM_CODEGENOPT(ZeroCallUsedRegs, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind,
492492
/// Whether to use opaque pointers.
493493
CODEGENOPT(OpaquePointers, 1, 0)
494494

495+
/// Modify C++ ABI to returning `this` pointer from constructors and
496+
/// non-deleting destructors. (No effect on Microsoft ABI.)
497+
CODEGENOPT(CtorDtorReturnThis, 1, 0)
498+
495499
#undef CODEGENOPT
496500
#undef ENUM_CODEGENOPT
497501
#undef VALUE_CODEGENOPT

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5604,6 +5604,11 @@ def ehcontguard : Flag<["-"], "ehcontguard">,
56045604
def fdenormal_fp_math_f32_EQ : Joined<["-"], "fdenormal-fp-math-f32=">,
56055605
Group<f_Group>;
56065606

5607+
def fctor_dtor_return_this : Flag<["-"], "fctor-dtor-return-this">,
5608+
HelpText<"Change the C++ ABI to returning `this` pointer from constructors "
5609+
"and non-deleting destructors. (No effect on Microsoft ABI)">,
5610+
MarshallingInfoFlag<CodeGenOpts<"CtorDtorReturnThis">>;
5611+
56075612
} // let Flags = [CC1Option, NoDriverOption]
56085613

56095614
//===----------------------------------------------------------------------===//

clang/lib/CodeGen/CGCXXABI.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ class CGCXXABI {
105105
/// final class will have been taken care of by the caller.
106106
virtual bool isThisCompleteObject(GlobalDecl GD) const = 0;
107107

108+
virtual bool constructorsAndDestructorsReturnThis() const {
109+
return CGM.getCodeGenOpts().CtorDtorReturnThis;
110+
}
111+
108112
public:
109113

110114
virtual ~CGCXXABI();
@@ -120,7 +124,13 @@ class CGCXXABI {
120124
///
121125
/// There currently is no way to indicate if a destructor returns 'this'
122126
/// when called virtually, and code generation does not support the case.
123-
virtual bool HasThisReturn(GlobalDecl GD) const { return false; }
127+
virtual bool HasThisReturn(GlobalDecl GD) const {
128+
if (isa<CXXConstructorDecl>(GD.getDecl()) ||
129+
(isa<CXXDestructorDecl>(GD.getDecl()) &&
130+
GD.getDtorType() != Dtor_Deleting))
131+
return constructorsAndDestructorsReturnThis();
132+
return false;
133+
}
124134

125135
virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; }
126136

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -433,11 +433,7 @@ class ARMCXXABI : public ItaniumCXXABI {
433433
ItaniumCXXABI(CGM, /*UseARMMethodPtrABI=*/true,
434434
/*UseARMGuardVarABI=*/true) {}
435435

436-
bool HasThisReturn(GlobalDecl GD) const override {
437-
return (isa<CXXConstructorDecl>(GD.getDecl()) || (
438-
isa<CXXDestructorDecl>(GD.getDecl()) &&
439-
GD.getDtorType() != Dtor_Deleting));
440-
}
436+
bool constructorsAndDestructorsReturnThis() const override { return true; }
441437

442438
void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV,
443439
QualType ResTy) override;
@@ -468,11 +464,7 @@ class FuchsiaCXXABI final : public ItaniumCXXABI {
468464
: ItaniumCXXABI(CGM) {}
469465

470466
private:
471-
bool HasThisReturn(GlobalDecl GD) const override {
472-
return isa<CXXConstructorDecl>(GD.getDecl()) ||
473-
(isa<CXXDestructorDecl>(GD.getDecl()) &&
474-
GD.getDtorType() != Dtor_Deleting);
475-
}
467+
bool constructorsAndDestructorsReturnThis() const override { return true; }
476468
};
477469

478470
class WebAssemblyCXXABI final : public ItaniumCXXABI {
@@ -486,11 +478,7 @@ class WebAssemblyCXXABI final : public ItaniumCXXABI {
486478
llvm::Value *Exn) override;
487479

488480
private:
489-
bool HasThisReturn(GlobalDecl GD) const override {
490-
return isa<CXXConstructorDecl>(GD.getDecl()) ||
491-
(isa<CXXDestructorDecl>(GD.getDecl()) &&
492-
GD.getDtorType() != Dtor_Deleting);
493-
}
481+
bool constructorsAndDestructorsReturnThis() const override { return true; }
494482
bool canCallMismatchedFunctionType() const override { return false; }
495483
};
496484

clang/test/CodeGenCXX/constructor-destructor-return-this.cpp

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
//RUN: %clang_cc1 -no-opaque-pointers %s -emit-llvm -o - -triple=x86_64-unknown-fuchsia | FileCheck --check-prefix=CHECKFUCHSIA %s
77
//RUN: %clang_cc1 -no-opaque-pointers %s -emit-llvm -o - -triple=aarch64-unknown-fuchsia | FileCheck --check-prefix=CHECKFUCHSIA %s
88
//RUN: %clang_cc1 -no-opaque-pointers %s -emit-llvm -o - -triple=i386-pc-win32 -fno-rtti | FileCheck --check-prefix=CHECKMS %s
9+
//RUN: %clang_cc1 -no-opaque-pointers %s -emit-llvm -o - -triple=i686-unknown-linux-gnu -fctor-dtor-return-this | FileCheck --check-prefix=CHECKI686RET %s
10+
//RUN: %clang_cc1 -no-opaque-pointers %s -emit-llvm -o - -triple=aarch64-unknown-linux-gnu -fctor-dtor-return-this | FileCheck --check-prefix=CHECKAARCH64RET %s
911
// FIXME: these tests crash on the bots when run with -triple=x86_64-pc-win32
1012

1113
// Make sure we attach the 'returned' attribute to the 'this' parameter of
@@ -37,10 +39,15 @@ B::~B() { }
3739
// CHECKGEN-LABEL: define{{.*}} void @_ZN1BD2Ev(%class.B* {{[^,]*}} %this)
3840
// CHECKGEN-LABEL: define{{.*}} void @_ZN1BD1Ev(%class.B* {{[^,]*}} %this)
3941

40-
// CHECKARM-LABEL: define{{.*}} %class.B* @_ZN1BC2EPi(%class.B* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i)
41-
// CHECKARM-LABEL: define{{.*}} %class.B* @_ZN1BC1EPi(%class.B* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i)
42-
// CHECKARM-LABEL: define{{.*}} %class.B* @_ZN1BD2Ev(%class.B* {{[^,]*}} returned{{[^,]*}} %this)
43-
// CHECKARM-LABEL: define{{.*}} %class.B* @_ZN1BD1Ev(%class.B* {{[^,]*}} returned{{[^,]*}} %this)
42+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.B* @_ZN1BC2EPi(%class.B* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i)
43+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.B* @_ZN1BC1EPi(%class.B* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i)
44+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.B* @_ZN1BD2Ev(%class.B* {{[^,]*}} returned{{[^,]*}} %this)
45+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.B* @_ZN1BD1Ev(%class.B* {{[^,]*}} returned{{[^,]*}} %this)
46+
47+
// CHECKI686RET-LABEL: define{{.*}} %class.B* @_ZN1BC2EPi(%class.B* {{[^,]*}} %this, i32* noundef %i)
48+
// CHECKI686RET-LABEL: define{{.*}} %class.B* @_ZN1BC1EPi(%class.B* {{[^,]*}} %this, i32* noundef %i)
49+
// CHECKI686RET-LABEL: define{{.*}} %class.B* @_ZN1BD2Ev(%class.B* {{[^,]*}} %this)
50+
// CHECKI686RET-LABEL: define{{.*}} %class.B* @_ZN1BD1Ev(%class.B* {{[^,]*}} %this)
4451

4552
// CHECKIOS5-LABEL: define{{.*}} %class.B* @_ZN1BC2EPi(%class.B* {{[^,]*}} %this, i32* noundef %i)
4653
// CHECKIOS5-LABEL: define{{.*}} %class.B* @_ZN1BC1EPi(%class.B* {{[^,]*}} %this, i32* noundef %i)
@@ -74,13 +81,23 @@ C::~C() { }
7481
// CHECKGEN-LABEL: define{{.*}} void @_ZN1CD0Ev(%class.C* {{[^,]*}} %this)
7582
// CHECKGEN-LABEL: define{{.*}} void @_ZThn8_N1CD0Ev(%class.C* noundef %this)
7683

77-
// CHECKARM-LABEL: define{{.*}} %class.C* @_ZN1CC2EPiPc(%class.C* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
78-
// CHECKARM-LABEL: define{{.*}} %class.C* @_ZN1CC1EPiPc(%class.C* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
79-
// CHECKARM-LABEL: define{{.*}} %class.C* @_ZN1CD2Ev(%class.C* {{[^,]*}} returned{{[^,]*}} %this)
80-
// CHECKARM-LABEL: define{{.*}} %class.C* @_ZN1CD1Ev(%class.C* {{[^,]*}} returned{{[^,]*}} %this)
84+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.C* @_ZN1CC2EPiPc(%class.C* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
85+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.C* @_ZN1CC1EPiPc(%class.C* {{[^,]*}} returned{{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
86+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.C* @_ZN1CD2Ev(%class.C* {{[^,]*}} returned{{[^,]*}} %this)
87+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.C* @_ZN1CD1Ev(%class.C* {{[^,]*}} returned{{[^,]*}} %this)
8188
// CHECKARM-LABEL: define{{.*}} %class.C* @_ZThn8_N1CD1Ev(%class.C* noundef %this)
82-
// CHECKARM-LABEL: define{{.*}} void @_ZN1CD0Ev(%class.C* {{[^,]*}} %this)
89+
// CHECKAARCH64RET-LABEL: define{{.*}} %class.C* @_ZThn16_N1CD1Ev(%class.C* noundef %this)
90+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} void @_ZN1CD0Ev(%class.C* {{[^,]*}} %this)
8391
// CHECKARM-LABEL: define{{.*}} void @_ZThn8_N1CD0Ev(%class.C* noundef %this)
92+
// CHECKAARCH64RET-LABEL: define{{.*}} void @_ZThn16_N1CD0Ev(%class.C* noundef %this)
93+
94+
// CHECKI686RET-LABEL: define{{.*}} %class.C* @_ZN1CC2EPiPc(%class.C* {{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
95+
// CHECKI686RET-LABEL: define{{.*}} %class.C* @_ZN1CC1EPiPc(%class.C* {{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
96+
// CHECKI686RET-LABEL: define{{.*}} %class.C* @_ZN1CD2Ev(%class.C* {{[^,]*}} %this)
97+
// CHECKI686RET-LABEL: define{{.*}} %class.C* @_ZN1CD1Ev(%class.C* {{[^,]*}} %this)
98+
// CHECKI686RET-LABEL: define{{.*}} %class.C* @_ZThn8_N1CD1Ev(%class.C* noundef %this)
99+
// CHECKI686RET-LABEL: define{{.*}} void @_ZN1CD0Ev(%class.C* {{[^,]*}} %this)
100+
// CHECKI686RET-LABEL: define{{.*}} void @_ZThn8_N1CD0Ev(%class.C* noundef %this)
84101

85102
// CHECKIOS5-LABEL: define{{.*}} %class.C* @_ZN1CC2EPiPc(%class.C* {{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
86103
// CHECKIOS5-LABEL: define{{.*}} %class.C* @_ZN1CC1EPiPc(%class.C* {{[^,]*}} %this, i32* noundef %i, i8* noundef %c)
@@ -115,10 +132,15 @@ D::~D() { }
115132
// CHECKGEN-LABEL: define{{.*}} void @_ZN1DD2Ev(%class.D* {{[^,]*}} %this, i8** noundef %vtt)
116133
// CHECKGEN-LABEL: define{{.*}} void @_ZN1DD1Ev(%class.D* {{[^,]*}} %this)
117134

118-
// CHECKARM-LABEL: define{{.*}} %class.D* @_ZN1DC2Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this, i8** noundef %vtt)
119-
// CHECKARM-LABEL: define{{.*}} %class.D* @_ZN1DC1Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this)
120-
// CHECKARM-LABEL: define{{.*}} %class.D* @_ZN1DD2Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this, i8** noundef %vtt)
121-
// CHECKARM-LABEL: define{{.*}} %class.D* @_ZN1DD1Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this)
135+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.D* @_ZN1DC2Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this, i8** noundef %vtt)
136+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.D* @_ZN1DC1Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this)
137+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.D* @_ZN1DD2Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this, i8** noundef %vtt)
138+
// CHECKARM-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} %class.D* @_ZN1DD1Ev(%class.D* {{[^,]*}} returned{{[^,]*}} %this)
139+
140+
// CHECKI686RET-LABEL: define{{.*}} %class.D* @_ZN1DC2Ev(%class.D* {{[^,]*}} %this, i8** noundef %vtt)
141+
// CHECKI686RET-LABEL: define{{.*}} %class.D* @_ZN1DC1Ev(%class.D* {{[^,]*}} %this)
142+
// CHECKI686RET-LABEL: define{{.*}} %class.D* @_ZN1DD2Ev(%class.D* {{[^,]*}} %this, i8** noundef %vtt)
143+
// CHECKI686RET-LABEL: define{{.*}} %class.D* @_ZN1DD1Ev(%class.D* {{[^,]*}} %this)
122144

123145
// CHECKIOS5-LABEL: define{{.*}} %class.D* @_ZN1DC2Ev(%class.D* {{[^,]*}} %this, i8** noundef %vtt)
124146
// CHECKIOS5-LABEL: define{{.*}} %class.D* @_ZN1DC1Ev(%class.D* {{[^,]*}} %this)
@@ -147,17 +169,17 @@ void test_destructor() {
147169
e2->~E();
148170
}
149171

150-
// CHECKARM-LABEL,CHECKFUCHSIA-LABEL: define{{.*}} void @_Z15test_destructorv()
172+
// CHECKARM-LABEL,CHECKFUCHSIA-LABEL,CHECKAARCH64RET-LABEL: define{{.*}} void @_Z15test_destructorv()
151173

152174
// Verify that virtual calls to destructors are not marked with a 'returned'
153175
// this parameter at the call site...
154-
// CHECKARM: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)*, %class.E* (%class.E*)**
155-
// CHECKARM: [[THUNK:%.*]] = load %class.E* (%class.E*)*, %class.E* (%class.E*)** [[VFN]]
176+
// CHECKARM,CHECKAARCH64RET: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)*, %class.E* (%class.E*)**
177+
// CHECKARM,CHECKAARCH64RET: [[THUNK:%.*]] = load %class.E* (%class.E*)*, %class.E* (%class.E*)** [[VFN]]
156178
// CHECKFUCHSIA: [[THUNK_I8:%.*]] = call i8* @llvm.load.relative.i32(i8* {{.*}}, i32 0)
157179
// CHECKFUCHSIA: [[THUNK:%.*]] = bitcast i8* [[THUNK_I8]] to %class.E* (%class.E*)*
158-
// CHECKARM,CHECKFUCHSIA: call noundef %class.E* [[THUNK]](%class.E* {{[^,]*}} %
180+
// CHECKARM,CHECKFUCHSIA,CHECKAARCH64RET: call noundef %class.E* [[THUNK]](%class.E* {{[^,]*}} %
159181

160182
// ...but static calls create declarations with 'returned' this
161-
// CHECKARM,CHECKFUCHSIA: {{%.*}} = call noundef %class.E* @_ZN1ED1Ev(%class.E* {{[^,]*}} %
183+
// CHECKARM,CHECKFUCHSIA,CHECKAARCH64RET: {{%.*}} = call noundef %class.E* @_ZN1ED1Ev(%class.E* {{[^,]*}} %
162184

163-
// CHECKARM,CHECKFUCHSIA: declare noundef %class.E* @_ZN1ED1Ev(%class.E* {{[^,]*}} returned{{[^,]*}})
185+
// CHECKARM,CHECKFUCHSIA,CHECKAARCH64RET: declare noundef %class.E* @_ZN1ED1Ev(%class.E* {{[^,]*}} returned{{[^,]*}})

0 commit comments

Comments
 (0)