Skip to content

Commit

Permalink
[CIR][CIRGen] Emit memcpys for copy constructors (#1197)
Browse files Browse the repository at this point in the history
CodeGen does so for trivial record types as well as non-record types; we
only do it for non-record types.
  • Loading branch information
smeenai authored Dec 3, 2024
1 parent 540b171 commit 165afb8
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 18 deletions.
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ struct MissingFeatures {
static bool shouldSplitConstantStore() { return false; }
static bool shouldCreateMemCpyFromGlobal() { return false; }
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
static bool fieldMemcpyizerBuildMemcpy() { return false; }
static bool isTrivialCtorOrDtor() { return false; }
static bool isMemcpyEquivalentSpecialMember() { return false; }
static bool constructABIArgDirectExtend() { return false; }
Expand Down
44 changes: 27 additions & 17 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,23 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
bool isMemberInitMemcpyable(CXXCtorInitializer *MemberInit) const {
if (!MemcpyableCtor)
return false;
FieldDecl *field = MemberInit->getMember();
assert(field && "No field for member init.");
QualType fieldType = field->getType();
CXXConstructExpr *ce = dyn_cast<CXXConstructExpr>(MemberInit->getInit());

// Bail out on any members of record type (unlike CodeGen, which emits a
// memcpy for trivially-copyable record types).
if (ce || (fieldType->isArrayType() &&
CGF.getContext().getBaseElementType(fieldType)->isRecordType()))
return false;

assert(!cir::MissingFeatures::fieldMemcpyizerBuildMemcpy());
return false;
// Bail out on volatile fields.
if (!isMemcpyableField(field))
return false;

// Otherwise we're good.
return true;
}

public:
Expand Down Expand Up @@ -363,7 +377,10 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
// This memcpy is too small to be worthwhile. Fall back on default
// codegen.
if (!AggregatedInits.empty()) {
llvm_unreachable("NYI");
CopyingValueRepresentation cvr(CGF);
emitMemberInitializer(CGF, ConstructorDecl->getParent(),
AggregatedInits[0], ConstructorDecl, Args);
AggregatedInits.clear();
}
reset();
return;
Expand All @@ -375,21 +392,14 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
}

void pushEHDestructors() {
Address ThisPtr = CGF.LoadCXXThisAddress();
QualType RecordTy = CGF.getContext().getTypeDeclType(ClassDecl);
LValue LHS = CGF.makeAddrLValue(ThisPtr, RecordTy);
(void)LHS;

for (unsigned i = 0; i < AggregatedInits.size(); ++i) {
CXXCtorInitializer *MemberInit = AggregatedInits[i];
QualType FieldType = MemberInit->getAnyMember()->getType();
QualType::DestructionKind dtorKind = FieldType.isDestructedType();
if (!CGF.needsEHCleanup(dtorKind))
continue;
LValue FieldLHS = LHS;
emitLValueForAnyFieldInitialization(CGF, MemberInit, FieldLHS);
CGF.pushEHDestroy(dtorKind, FieldLHS.getAddress(), FieldType);
#ifndef NDEBUG
for (CXXCtorInitializer *memberInit : AggregatedInits) {
QualType fieldType = memberInit->getAnyMember()->getType();
QualType::DestructionKind dtorKind = fieldType.isDestructedType();
assert(!CGF.needsEHCleanup(dtorKind) &&
"Non-record types shouldn't need EH cleanup");
}
#endif
}

void finish() { emitAggregatedInits(); }
Expand Down
52 changes: 52 additions & 0 deletions clang/test/CIR/CodeGen/copy-constructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,55 @@ struct HasScalarArrayMember {
// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %[[#THIS_ARR]], ptr %[[#OTHER_ARR]], i32 16, i1 false)
// LLVM-NEXT: ret void
HasScalarArrayMember::HasScalarArrayMember(const HasScalarArrayMember &) = default;

struct Trivial { int *i; };
struct ManyMembers {
int i;
int j;
Trivial k;
int l[1];
int m[2];
Trivial n;
int &o;
int *p;
};

// CIR-LABEL: cir.func linkonce_odr @_ZN11ManyMembersC2ERKS_(
// CIR: %[[#THIS_LOAD:]] = cir.load %[[#]]
// CIR-NEXT: %[[#THIS_I:]] = cir.get_member %[[#THIS_LOAD]][0] {name = "i"}
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER:]]
// CIR-NEXT: %[[#OTHER_I:]] = cir.get_member %[[#OTHER_LOAD]][0] {name = "i"}
// CIR-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<8>
// CIR-NEXT: %[[#THIS_I_CAST:]] = cir.cast(bitcast, %[[#THIS_I]] : !cir.ptr<!s32i>), !cir.ptr<!void>
// CIR-NEXT: %[[#OTHER_I_CAST:]] = cir.cast(bitcast, %[[#OTHER_I]] : !cir.ptr<!s32i>), !cir.ptr<!void>
// CIR-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_I_CAST]] to %[[#THIS_I_CAST]]
// CIR-NEXT: %[[#THIS_K:]] = cir.get_member %[[#THIS_LOAD]][2] {name = "k"}
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
// CIR-NEXT: %[[#OTHER_K:]] = cir.get_member %[[#OTHER_LOAD]][2] {name = "k"}
// CIR-NEXT: cir.call @_ZN7TrivialC1ERKS_(%[[#THIS_K]], %[[#OTHER_K]])
// CIR-NEXT: %[[#THIS_L:]] = cir.get_member %[[#THIS_LOAD]][3] {name = "l"}
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
// CIR-NEXT: %[[#OTHER_L:]] = cir.get_member %[[#OTHER_LOAD]][3] {name = "l"}
// CIR-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<12>
// CIR-NEXT: %[[#THIS_L_CAST:]] = cir.cast(bitcast, %[[#THIS_L]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!void>
// CIR-NEXT: %[[#OTHER_L_CAST:]] = cir.cast(bitcast, %[[#OTHER_L]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!void>
// CIR-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_L_CAST]] to %[[#THIS_L_CAST]]
// CIR-NEXT: %[[#THIS_N:]] = cir.get_member %[[#THIS_LOAD]][5] {name = "n"}
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
// CIR-NEXT: %[[#OTHER_N:]] = cir.get_member %[[#OTHER_LOAD]][5] {name = "n"}
// CIR-NEXT: cir.call @_ZN7TrivialC1ERKS_(%[[#THIS_N]], %[[#OTHER_N]])
// CIR-NEXT: %[[#THIS_O:]] = cir.get_member %[[#THIS_LOAD]][6] {name = "o"}
// CIR-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#OTHER]]
// CIR-NEXT: %[[#OTHER_O:]] = cir.get_member %[[#OTHER_LOAD]][6] {name = "o"}
// CIR-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<16>
// CIR-NEXT: %[[#THIS_O_CAST:]] = cir.cast(bitcast, %[[#THIS_O]] : !cir.ptr<!cir.ptr<!s32i>>), !cir.ptr<!void>
// CIR-NEXT: %[[#OTHER_O_CAST:]] = cir.cast(bitcast, %[[#OTHER_O]] : !cir.ptr<!cir.ptr<!s32i>>), !cir.ptr<!void>
// CIR-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_O_CAST]] to %[[#THIS_O_CAST]]
// CIR-NEXT: cir.return
// CIR-NEXT: }

// CIR-LABEL: cir.func @_Z6doCopyR11ManyMembers(
// CIR: cir.call @_ZN11ManyMembersC1ERKS_(
ManyMembers doCopy(ManyMembers &src) {
return src;
}

0 comments on commit 165afb8

Please sign in to comment.