Skip to content

Commit 165afb8

Browse files
authored
[CIR][CIRGen] Emit memcpys for copy constructors (#1197)
CodeGen does so for trivial record types as well as non-record types; we only do it for non-record types.
1 parent 540b171 commit 165afb8

File tree

3 files changed

+79
-18
lines changed

3 files changed

+79
-18
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ struct MissingFeatures {
141141
static bool shouldSplitConstantStore() { return false; }
142142
static bool shouldCreateMemCpyFromGlobal() { return false; }
143143
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
144-
static bool fieldMemcpyizerBuildMemcpy() { return false; }
145144
static bool isTrivialCtorOrDtor() { return false; }
146145
static bool isMemcpyEquivalentSpecialMember() { return false; }
147146
static bool constructABIArgDirectExtend() { return false; }

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,23 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
333333
bool isMemberInitMemcpyable(CXXCtorInitializer *MemberInit) const {
334334
if (!MemcpyableCtor)
335335
return false;
336+
FieldDecl *field = MemberInit->getMember();
337+
assert(field && "No field for member init.");
338+
QualType fieldType = field->getType();
339+
CXXConstructExpr *ce = dyn_cast<CXXConstructExpr>(MemberInit->getInit());
340+
341+
// Bail out on any members of record type (unlike CodeGen, which emits a
342+
// memcpy for trivially-copyable record types).
343+
if (ce || (fieldType->isArrayType() &&
344+
CGF.getContext().getBaseElementType(fieldType)->isRecordType()))
345+
return false;
336346

337-
assert(!cir::MissingFeatures::fieldMemcpyizerBuildMemcpy());
338-
return false;
347+
// Bail out on volatile fields.
348+
if (!isMemcpyableField(field))
349+
return false;
350+
351+
// Otherwise we're good.
352+
return true;
339353
}
340354

341355
public:
@@ -363,7 +377,10 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
363377
// This memcpy is too small to be worthwhile. Fall back on default
364378
// codegen.
365379
if (!AggregatedInits.empty()) {
366-
llvm_unreachable("NYI");
380+
CopyingValueRepresentation cvr(CGF);
381+
emitMemberInitializer(CGF, ConstructorDecl->getParent(),
382+
AggregatedInits[0], ConstructorDecl, Args);
383+
AggregatedInits.clear();
367384
}
368385
reset();
369386
return;
@@ -375,21 +392,14 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
375392
}
376393

377394
void pushEHDestructors() {
378-
Address ThisPtr = CGF.LoadCXXThisAddress();
379-
QualType RecordTy = CGF.getContext().getTypeDeclType(ClassDecl);
380-
LValue LHS = CGF.makeAddrLValue(ThisPtr, RecordTy);
381-
(void)LHS;
382-
383-
for (unsigned i = 0; i < AggregatedInits.size(); ++i) {
384-
CXXCtorInitializer *MemberInit = AggregatedInits[i];
385-
QualType FieldType = MemberInit->getAnyMember()->getType();
386-
QualType::DestructionKind dtorKind = FieldType.isDestructedType();
387-
if (!CGF.needsEHCleanup(dtorKind))
388-
continue;
389-
LValue FieldLHS = LHS;
390-
emitLValueForAnyFieldInitialization(CGF, MemberInit, FieldLHS);
391-
CGF.pushEHDestroy(dtorKind, FieldLHS.getAddress(), FieldType);
395+
#ifndef NDEBUG
396+
for (CXXCtorInitializer *memberInit : AggregatedInits) {
397+
QualType fieldType = memberInit->getAnyMember()->getType();
398+
QualType::DestructionKind dtorKind = fieldType.isDestructedType();
399+
assert(!CGF.needsEHCleanup(dtorKind) &&
400+
"Non-record types shouldn't need EH cleanup");
392401
}
402+
#endif
393403
}
394404

395405
void finish() { emitAggregatedInits(); }

clang/test/CIR/CodeGen/copy-constructor.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,55 @@ struct HasScalarArrayMember {
3333
// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %[[#THIS_ARR]], ptr %[[#OTHER_ARR]], i32 16, i1 false)
3434
// LLVM-NEXT: ret void
3535
HasScalarArrayMember::HasScalarArrayMember(const HasScalarArrayMember &) = default;
36+
37+
struct Trivial { int *i; };
38+
struct ManyMembers {
39+
int i;
40+
int j;
41+
Trivial k;
42+
int l[1];
43+
int m[2];
44+
Trivial n;
45+
int &o;
46+
int *p;
47+
};
48+
49+
// CIR-LABEL: cir.func linkonce_odr @_ZN11ManyMembersC2ERKS_(
50+
// CIR: %[[#THIS_LOAD:]] = cir.load %[[#]]
51+
// CIR-NEXT: %[[#THIS_I:]] = cir.get_member %[[#THIS_LOAD]][0] {name = "i"}
52+
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER:]]
53+
// CIR-NEXT: %[[#OTHER_I:]] = cir.get_member %[[#OTHER_LOAD]][0] {name = "i"}
54+
// CIR-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<8>
55+
// CIR-NEXT: %[[#THIS_I_CAST:]] = cir.cast(bitcast, %[[#THIS_I]] : !cir.ptr<!s32i>), !cir.ptr<!void>
56+
// CIR-NEXT: %[[#OTHER_I_CAST:]] = cir.cast(bitcast, %[[#OTHER_I]] : !cir.ptr<!s32i>), !cir.ptr<!void>
57+
// CIR-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_I_CAST]] to %[[#THIS_I_CAST]]
58+
// CIR-NEXT: %[[#THIS_K:]] = cir.get_member %[[#THIS_LOAD]][2] {name = "k"}
59+
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
60+
// CIR-NEXT: %[[#OTHER_K:]] = cir.get_member %[[#OTHER_LOAD]][2] {name = "k"}
61+
// CIR-NEXT: cir.call @_ZN7TrivialC1ERKS_(%[[#THIS_K]], %[[#OTHER_K]])
62+
// CIR-NEXT: %[[#THIS_L:]] = cir.get_member %[[#THIS_LOAD]][3] {name = "l"}
63+
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
64+
// CIR-NEXT: %[[#OTHER_L:]] = cir.get_member %[[#OTHER_LOAD]][3] {name = "l"}
65+
// CIR-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<12>
66+
// CIR-NEXT: %[[#THIS_L_CAST:]] = cir.cast(bitcast, %[[#THIS_L]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!void>
67+
// CIR-NEXT: %[[#OTHER_L_CAST:]] = cir.cast(bitcast, %[[#OTHER_L]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!void>
68+
// CIR-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_L_CAST]] to %[[#THIS_L_CAST]]
69+
// CIR-NEXT: %[[#THIS_N:]] = cir.get_member %[[#THIS_LOAD]][5] {name = "n"}
70+
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
71+
// CIR-NEXT: %[[#OTHER_N:]] = cir.get_member %[[#OTHER_LOAD]][5] {name = "n"}
72+
// CIR-NEXT: cir.call @_ZN7TrivialC1ERKS_(%[[#THIS_N]], %[[#OTHER_N]])
73+
// CIR-NEXT: %[[#THIS_O:]] = cir.get_member %[[#THIS_LOAD]][6] {name = "o"}
74+
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
75+
// CIR-NEXT: %[[#OTHER_O:]] = cir.get_member %[[#OTHER_LOAD]][6] {name = "o"}
76+
// CIR-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<16>
77+
// CIR-NEXT: %[[#THIS_O_CAST:]] = cir.cast(bitcast, %[[#THIS_O]] : !cir.ptr<!cir.ptr<!s32i>>), !cir.ptr<!void>
78+
// CIR-NEXT: %[[#OTHER_O_CAST:]] = cir.cast(bitcast, %[[#OTHER_O]] : !cir.ptr<!cir.ptr<!s32i>>), !cir.ptr<!void>
79+
// CIR-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_O_CAST]] to %[[#THIS_O_CAST]]
80+
// CIR-NEXT: cir.return
81+
// CIR-NEXT: }
82+
83+
// CIR-LABEL: cir.func @_Z6doCopyR11ManyMembers(
84+
// CIR: cir.call @_ZN11ManyMembersC1ERKS_(
85+
ManyMembers doCopy(ManyMembers &src) {
86+
return src;
87+
}

0 commit comments

Comments
 (0)