Skip to content

Commit

Permalink
[CIR][CIRGen] Call trivial assignment operators in more cases (#1196)
Browse files Browse the repository at this point in the history
Our previous logic here was matching CodeGen, which folds trivial
assignment operator calls into memcpys, but we want to avoid that. Note
that we still end up emitting memcpys for arrays of classes with trivial
assignment operators; #1177 tracks
fixing that.
  • Loading branch information
smeenai authored Dec 3, 2024
1 parent baad05f commit 540b171
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 13 deletions.
17 changes: 4 additions & 13 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,20 +429,11 @@ class AssignmentMemcpyizer : public FieldMemcpyizer {
}
return nullptr;
} else if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(S)) {
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MCE->getCalleeDecl());
if (!(MD && isMemcpyEquivalentSpecialMember(MD)))
return nullptr;
MemberExpr *IOA = dyn_cast<MemberExpr>(MCE->getImplicitObjectArgument());
if (!IOA)
return nullptr;
FieldDecl *Field = dyn_cast<FieldDecl>(IOA->getMemberDecl());
if (!Field || !isMemcpyableField(Field))
return nullptr;
MemberExpr *Arg0 = dyn_cast<MemberExpr>(MCE->getArg(0));
if (!Arg0 || Field != dyn_cast<FieldDecl>(Arg0->getMemberDecl()))
return nullptr;
return Field;
// We want to represent all calls explicitly for analysis purposes.
return nullptr;
} else if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
// TODO(cir): https://github.com/llvm/clangir/issues/1177: This can result
// in memcpys instead of calls to trivial member functions.
FunctionDecl *FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
if (!FD || FD->getBuiltinID() != Builtin::BI__builtin_memcpy)
return nullptr;
Expand Down
30 changes: 30 additions & 0 deletions clang/test/CIR/CodeGen/assign-operator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,33 @@ struct Trivial {
void copyTrivial(Trivial &a, Trivial &b) {
a = b;
}

struct ContainsTrivial {
Trivial t1;
Trivial t2;
ContainsTrivial &operator=(const ContainsTrivial &);
};

// We should explicitly call operator= even for trivial types.
// CHECK-LABEL: cir.func @_ZN15ContainsTrivialaSERKS_(
// CHECK: cir.call @_ZN7TrivialaSERKS_(
// CHECK: cir.call @_ZN7TrivialaSERKS_(
ContainsTrivial &ContainsTrivial::operator=(const ContainsTrivial &) = default;

struct ContainsTrivialArray {
Trivial arr[2];
ContainsTrivialArray &operator=(const ContainsTrivialArray &);
};

// We should be calling operator= here but don't currently.
// CHECK-LABEL: cir.func @_ZN20ContainsTrivialArrayaSERKS_(
// CHECK: %[[#THIS_LOAD:]] = cir.load deref %[[#]]
// CHECK-NEXT: %[[#THIS_ARR:]] = cir.get_member %[[#THIS_LOAD]][0] {name = "arr"}
// CHECK-NEXT: %[[#THIS_ARR_CAST:]] = cir.cast(bitcast, %[[#THIS_ARR]] : !cir.ptr<!cir.array<!ty_Trivial x 2>>), !cir.ptr<!void>
// CHECK-NEXT: %[[#OTHER_LOAD:]] = cir.load %[[#]]
// CHECK-NEXT: %[[#OTHER_ARR:]] = cir.get_member %[[#OTHER_LOAD]][0] {name = "arr"}
// CHECK-NEXT: %[[#OTHER_ARR_CAST:]] = cir.cast(bitcast, %[[#OTHER_ARR]] : !cir.ptr<!cir.array<!ty_Trivial x 2>>), !cir.ptr<!void>
// CHECK-NEXT: %[[#MEMCPY_SIZE:]] = cir.const #cir.int<80> : !u64i
// CHECK-NEXT: cir.libc.memcpy %[[#MEMCPY_SIZE]] bytes from %[[#OTHER_ARR_CAST]] to %[[#THIS_ARR_CAST]]
ContainsTrivialArray &
ContainsTrivialArray::operator=(const ContainsTrivialArray &) = default;

0 comments on commit 540b171

Please sign in to comment.