[clang] [CIR] Implement isMemcpyEquivalentSpecialMember for trivial copy/move ctors (PR #186700)
Henrich Lauko via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 21 04:23:54 PDT 2026
https://github.com/xlauko updated https://github.com/llvm/llvm-project/pull/186700
>From a5dd902ce47fc67f2571ba8d9062c98301c27240 Mon Sep 17 00:00:00 2001
From: xlauko <xlauko at mail.muni.cz>
Date: Sun, 15 Mar 2026 16:44:06 +0100
Subject: [PATCH] [CIR] Implement isMemcpyEquivalentSpecialMember for trivial
copy/move ctors
---
clang/include/clang/AST/ASTContext.h | 4 ++
clang/include/clang/AST/DeclCXX.h | 9 ++++
clang/include/clang/CIR/MissingFeatures.h | 1 -
clang/lib/AST/ASTContext.cpp | 22 ++++++++++
clang/lib/AST/DeclCXX.cpp | 11 +++++
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 27 ++++++++----
clang/lib/CodeGen/CGClass.cpp | 38 ++++-------------
.../CIR/CodeGen/copy-constructor-memcpy.cpp | 41 +++++++++++++++++++
clang/test/CIR/CodeGen/coro-task.cpp | 1 -
.../CIR/CodeGen/cxx-special-member-attr.cpp | 11 ++---
clang/test/CIR/CodeGen/nrvo.cpp | 2 +-
.../combined-firstprivate-clause.cpp | 6 ---
.../compute-firstprivate-clause-templates.cpp | 2 -
.../compute-firstprivate-clause.cpp | 6 ---
14 files changed, 121 insertions(+), 60 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/copy-constructor-memcpy.cpp
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 40091acd878fd..da9e301a336d2 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3834,6 +3834,10 @@ OPT_LIST(V)
StringRef getCUIDHash() const;
+ /// Returns whether D is a copy/move constructor or assignment operator
+ /// that can be implemented as a memcpy of the object representation.
+ bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) const;
+
/// Returns a list of PFP fields for the given type, including subfields in
/// bases or other fields, except for fields contained within fields of union
/// type.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 48fd12efdcafe..099f096dd9309 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -2229,6 +2229,15 @@ class CXXMethodDecl : public FunctionDecl {
/// Determine whether this is a move assignment operator.
bool isMoveAssignmentOperator() const;
+ /// Determine whether this is a copy or move constructor or a copy or move
+ /// assignment operator.
+ bool isCopyOrMoveConstructorOrAssignment() const;
+
+ /// Determine whether this is a copy or move constructor. Always returns
+ /// false for non-constructor methods; see also
+ /// CXXConstructorDecl::isCopyOrMoveConstructor().
+ bool isCopyOrMoveConstructor() const;
+
CXXMethodDecl *getCanonicalDecl() override {
return cast<CXXMethodDecl>(FunctionDecl::getCanonicalDecl());
}
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 68db08a5580ca..f029448cd0e2d 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -290,7 +290,6 @@ struct MissingFeatures {
static bool instrumentation() { return false; }
static bool intrinsicElementTypeSupport() { return false; }
static bool intrinsics() { return false; }
- static bool isMemcpyEquivalentSpecialMember() { return false; }
static bool isTrivialCtorOrDtor() { return false; }
static bool lambdaCaptures() { return false; }
static bool loopInfoStack() { return false; }
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a8cfdca1cb96d..0a76d577e369e 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15480,6 +15480,28 @@ bool ASTContext::arePFPFieldsTriviallyCopyable(const RecordDecl *RD) const {
return true;
}
+bool ASTContext::isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) const {
+ if (!D->isCopyOrMoveConstructorOrAssignment())
+ return false;
+
+ // Non-trivially-copyable fields with pointer field protection need to be
+ // copied one by one.
+ const CXXRecordDecl *Parent = D->getParent();
+ if (!arePFPFieldsTriviallyCopyable(Parent) &&
+ hasPFPFields(getCanonicalTagType(Parent)))
+ return false;
+
+ // We can emit a memcpy for a trivial copy or move constructor/assignment.
+ if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding())
+ return true;
+
+ // We *must* emit a memcpy for a defaulted union copy or move op.
+ if (D->getParent()->isUnion() && D->isDefaulted())
+ return true;
+
+ return false;
+}
+
static void findPFPFields(const ASTContext &Ctx, QualType Ty, CharUnits Offset,
std::vector<PFPField> &Fields, bool IncludeVBases) {
if (auto *AT = Ctx.getAsConstantArrayType(Ty)) {
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 083c53e28cb91..c05514fef5fd6 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2770,6 +2770,17 @@ bool CXXMethodDecl::isMoveAssignmentOperator() const {
return Context.hasSameUnqualifiedType(ClassType, ParamType);
}
+bool CXXMethodDecl::isCopyOrMoveConstructor() const {
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(this))
+ return Ctor->isCopyOrMoveConstructor();
+ return false;
+}
+
+bool CXXMethodDecl::isCopyOrMoveConstructorOrAssignment() const {
+ return isCopyOrMoveConstructor() || isCopyAssignmentOperator() ||
+ isMoveAssignmentOperator();
+}
+
void CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *MD) {
assert(MD->isCanonicalDecl() && "Method is not canonical!");
assert(MD->isVirtual() && "Method is not virtual!");
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 7dca65eff4f7c..9b97f0d31db32 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -1279,25 +1279,37 @@ mlir::Value CIRGenFunction::getVTablePtr(mlir::Location loc, Address thisAddr,
return vtable;
}
+static bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *d) {
+ return d->getASTContext().isMemcpyEquivalentSpecialMember(d);
+}
+
void CIRGenFunction::emitCXXConstructorCall(const clang::CXXConstructorDecl *d,
clang::CXXCtorType type,
bool forVirtualBase,
bool delegating,
AggValueSlot thisAVS,
const clang::CXXConstructExpr *e) {
- CallArgList args;
Address thisAddr = thisAVS.getAddress();
QualType thisType = d->getThisType();
mlir::Value thisPtr = thisAddr.getPointer();
assert(!cir::MissingFeatures::addressSpace());
- args.add(RValue::get(thisPtr), thisType);
+ // If this is a trivial constructor, just emit what's needed. If this is a
+ // union copy constructor, we must emit a memcpy, because the AST does not
+ // model that copy.
+ if (isMemcpyEquivalentSpecialMember(d)) {
+ assert(e->getNumArgs() == 1 && "unexpected argcount for trivial ctor");
+ const Expr *arg = e->getArg(0);
+ LValue src = emitLValue(arg);
+ CanQualType destTy = getContext().getCanonicalTagType(d->getParent());
+ LValue dest = makeAddrLValue(thisAddr, destTy);
+ emitAggregateCopy(dest, src, src.getType(), thisAVS.mayOverlap());
+ return;
+ }
- // In LLVM Codegen: If this is a trivial constructor, just emit what's needed.
- // If this is a union copy constructor, we must emit a memcpy, because the AST
- // does not model that copy.
- assert(!cir::MissingFeatures::isMemcpyEquivalentSpecialMember());
+ CallArgList args;
+ args.add(RValue::get(thisPtr), thisType);
const FunctionProtoType *fpt = d->getType()->castAs<FunctionProtoType>();
@@ -1323,7 +1335,8 @@ void CIRGenFunction::emitCXXConstructorCall(
// ctor call into trivial initialization.
assert(!cir::MissingFeatures::isTrivialCtorOrDtor());
- assert(!cir::MissingFeatures::isMemcpyEquivalentSpecialMember());
+ // Note: memcpy-equivalent special members are handled in the
+ // emitCXXConstructorCall overload that takes a CXXConstructExpr.
bool passPrototypeArgs = true;
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 6572009fc6003..5ac2fbcadf951 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -570,30 +570,8 @@ static void EmitBaseInitializer(CodeGenFunction &CGF,
isBaseVirtual);
}
-static bool isMemcpyEquivalentSpecialMember(CodeGenModule &CGM,
- const CXXMethodDecl *D) {
- auto *CD = dyn_cast<CXXConstructorDecl>(D);
- if (!(CD && CD->isCopyOrMoveConstructor()) &&
- !D->isCopyAssignmentOperator() && !D->isMoveAssignmentOperator())
- return false;
-
- // Non-trivially-copyable fields with pointer field protection need to be
- // copied one by one.
- ASTContext &Ctx = CGM.getContext();
- const CXXRecordDecl *Parent = D->getParent();
- if (!Ctx.arePFPFieldsTriviallyCopyable(Parent) &&
- Ctx.hasPFPFields(Ctx.getCanonicalTagType(Parent)))
- return false;
-
- // We can emit a memcpy for a trivial copy or move constructor/assignment.
- if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding())
- return true;
-
- // We *must* emit a memcpy for a defaulted union copy or move op.
- if (D->getParent()->isUnion() && D->isDefaulted())
- return true;
-
- return false;
+static bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) {
+ return D->getASTContext().isMemcpyEquivalentSpecialMember(D);
}
static void EmitLValueForAnyFieldInitialization(CodeGenFunction &CGF,
@@ -650,8 +628,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF,
QualType BaseElementTy = CGF.getContext().getBaseElementType(Array);
CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit());
if (BaseElementTy.isPODType(CGF.getContext()) ||
- (CE &&
- isMemcpyEquivalentSpecialMember(CGF.CGM, CE->getConstructor()))) {
+ (CE && isMemcpyEquivalentSpecialMember(CE->getConstructor()))) {
unsigned SrcArgIndex =
CGF.CGM.getCXXABI().getSrcArgforCopyCtor(Constructor, Args);
llvm::Value *SrcPtr =
@@ -1059,8 +1036,7 @@ class ConstructorMemcpyizer : public FieldMemcpyizer {
CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit());
// Bail out on non-memcpyable, not-trivially-copyable members.
- if (!(CE &&
- isMemcpyEquivalentSpecialMember(CGF.CGM, CE->getConstructor())) &&
+ if (!(CE && isMemcpyEquivalentSpecialMember(CE->getConstructor())) &&
!(FieldType.isTriviallyCopyableType(CGF.getContext()) ||
FieldType->isReferenceType()))
return false;
@@ -1169,7 +1145,7 @@ class AssignmentMemcpyizer : public FieldMemcpyizer {
return nullptr;
} else if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(S)) {
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MCE->getCalleeDecl());
- if (!(MD && isMemcpyEquivalentSpecialMember(CGF.CGM, MD)))
+ if (!(MD && isMemcpyEquivalentSpecialMember(MD)))
return nullptr;
MemberExpr *IOA = dyn_cast<MemberExpr>(MCE->getImplicitObjectArgument());
if (!IOA)
@@ -2304,7 +2280,7 @@ void CodeGenFunction::EmitCXXConstructorCall(
// If this is a trivial constructor, emit a memcpy now before we lose
// the alignment information on the argument.
// FIXME: It would be better to preserve alignment information into CallArg.
- if (isMemcpyEquivalentSpecialMember(CGM, D)) {
+ if (isMemcpyEquivalentSpecialMember(D)) {
assert(E->getNumArgs() == 1 && "unexpected argcount for trivial ctor");
const Expr *Arg = E->getArg(0);
@@ -2372,7 +2348,7 @@ void CodeGenFunction::EmitCXXConstructorCall(
// If this is a trivial constructor, just emit what's needed. If this is a
// union copy constructor, we must emit a memcpy, because the AST does not
// model that copy.
- if (isMemcpyEquivalentSpecialMember(CGM, D)) {
+ if (isMemcpyEquivalentSpecialMember(D)) {
assert(Args.size() == 2 && "unexpected argcount for trivial ctor");
QualType SrcTy = D->getParamDecl(0)->getType().getNonReferenceType();
Address Src = makeNaturalAddressForPointer(
diff --git a/clang/test/CIR/CodeGen/copy-constructor-memcpy.cpp b/clang/test/CIR/CodeGen/copy-constructor-memcpy.cpp
new file mode 100644
index 0000000000000..d7b141cd91bc9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/copy-constructor-memcpy.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu \
+// RUN: -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu \
+// RUN: -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu \
+// RUN: -emit-llvm %s -o %t.og.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.og.ll %s
+
+// Test that trivial copy constructors are inlined as aggregate copies
+// (memcpy-equivalent special members) rather than emitted as function calls.
+
+struct S {
+ int a;
+ int b;
+};
+
+struct W {
+ S s;
+ W(const S &src) : s(src) {}
+};
+
+void test(const S &src) {
+ W w(src);
+}
+
+// The copy of S in W's constructor should be inlined as cir.copy,
+// not a call to S's copy constructor.
+
+// CIR-LABEL: cir.func{{.*}} @_ZN1WC2ERK1S
+// CIR-NOT: cir.call @_ZN1SC
+// CIR: cir.copy %{{.+}} to %{{.+}} : !cir.ptr<!rec_S>
+
+// Both CIR-lowered LLVM and OG produce memcpy for the inlined copy.
+
+// LLVM-LABEL: define{{.*}} void @_ZN1WC2ERK1S
+// LLVM: call void @llvm.memcpy.p0.p0.i64({{.*}}i64 8
+
+// OGCG-LABEL: define{{.*}} void @_ZN1WC2ERK1S
+// OGCG: call void @llvm.memcpy.p0.p0.i64({{.*}}i64 8
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
index b52f0f1871079..568dedf2c7921 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -430,7 +430,6 @@ folly::coro::Task<void> yield1() {
// yield_value + await(yield)
// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_ADDR]]
-// CIR: cir.copy %[[T_ADDR]] to %[[AWAITER_COPY_ADDR]]
// CIR: %[[AWAITER:.*]] = cir.load{{.*}} %[[AWAITER_COPY_ADDR]]
// CIR: %[[YIELD_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]], %[[AWAITER]]){{.*}}
// CIR: cir.store{{.*}} %[[YIELD_SUSP]], %[[SUSP1]]
diff --git a/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp
index a68d8a5d48209..bfca59db44fdf 100644
--- a/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp
+++ b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp
@@ -30,21 +30,22 @@ struct Foo {
~Foo();
};
+// Trivial copy/move assignment operator definitions appear at module level.
+// CIR: @_ZN4FlubaSERKS_(%arg0: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Flub>{{.*}}) special_member<#cir.cxx_assign<!rec_Flub, copy, trivial true>>
+// CIR: @_ZN4FlubaSEOS_(%arg0: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Flub>{{.*}}) special_member<#cir.cxx_assign<!rec_Flub, move, trivial true>>
+
void trivial_func() {
Flub f1{};
Flub f2 = f1;
- // Trivial copy constructors/assignments are replaced with cir.copy
+ // Trivial copy/move constructors are inlined as cir.copy
// CIR: cir.copy {{.*}} : !cir.ptr<!rec_Flub>
Flub f3 = static_cast<Flub&&>(f1);
- // CIR: @_ZN4FlubC1EOS_(%arg0: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Flub, move, trivial true>
+ // CIR: cir.copy {{.*}} : !cir.ptr<!rec_Flub>
f2 = f1;
- // CIR: @_ZN4FlubaSERKS_(%arg0: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Flub>{{.*}}) special_member<#cir.cxx_assign<!rec_Flub, copy, trivial true>>
-
f1 = static_cast<Flub&&>(f3);
- // CIR: @_ZN4FlubaSEOS_(%arg0: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Flub>{{.*}}) special_member<#cir.cxx_assign<!rec_Flub, move, trivial true>>
}
void non_trivial_func() {
diff --git a/clang/test/CIR/CodeGen/nrvo.cpp b/clang/test/CIR/CodeGen/nrvo.cpp
index a3af2d1ff032b..08aad0723d331 100644
--- a/clang/test/CIR/CodeGen/nrvo.cpp
+++ b/clang/test/CIR/CodeGen/nrvo.cpp
@@ -32,7 +32,7 @@ struct S f1() {
// CIR-NOELIDE-NEXT: %[[RETVAL:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["__retval"]
// CIR-NOELIDE-NEXT: %[[S:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s", init]
// CIR-NOELIDE-NEXT: cir.call @_ZN1SC1Ev(%[[S]]) : (!cir.ptr<!rec_S> {{.*}}) -> ()
-// CIR-NOELIDE-NEXT: cir.call @_ZN1SC1EOS_(%[[RETVAL]], %[[S]]){{.*}} : (!cir.ptr<!rec_S> {{.*}}, !cir.ptr<!rec_S> {{.*}}) -> ()
+// CIR-NOELIDE-NEXT: cir.copy %[[S]] to %[[RETVAL]] : !cir.ptr<!rec_S>
// CIR-NOELIDE-NEXT: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr<!rec_S>, !rec_S
// CIR-NOELIDE-NEXT: cir.return %[[RET]]
diff --git a/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp b/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp
index 0a67314a91b07..5c48fb3088431 100644
--- a/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp
@@ -43,7 +43,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_NoCopyConstruct> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_NoCopyConstruct> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: }
//
@@ -63,7 +62,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_NonDefaultCtor> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_NonDefaultCtor> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: }
//
@@ -73,7 +71,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } destroy {
// CHECK-NEXT: ^bb0(%[[ORIG:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}, %[[ARG:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}):
@@ -176,7 +173,6 @@ struct HasDtor {
// CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NoCopyConstruct>, !u64i) -> !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>> -> !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NoCopyConstruct>, !u64i) -> !cir.ptr<!rec_NoCopyConstruct>
-// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] : !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr<!u64i>, !u64i
@@ -246,7 +242,6 @@ struct HasDtor {
// CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NonDefaultCtor>, !u64i) -> !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_NonDefaultCtor x 5>> -> !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NonDefaultCtor>, !u64i) -> !cir.ptr<!rec_NonDefaultCtor>
-// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] : !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr<!u64i>, !u64i
@@ -281,7 +276,6 @@ struct HasDtor {
// CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr<!rec_HasDtor>, !u64i) -> !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_HasDtor x 5>> -> !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr<!rec_HasDtor>, !u64i) -> !cir.ptr<!rec_HasDtor>
-// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] : !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr<!u64i>, !u64i
diff --git a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp
index 4592d42d37631..55734ae4bc18a 100644
--- a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp
@@ -30,7 +30,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_NonDefaultCtor> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_NonDefaultCtor> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: }
//
@@ -40,7 +39,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } destroy {
// CHECK-NEXT: ^bb0(%[[ORIG:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}, %[[ARG:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}):
diff --git a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp
index c4cec3023e046..033dc4cef1134 100644
--- a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp
@@ -44,7 +44,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_NoCopyConstruct> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_NoCopyConstruct> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: }
//
@@ -64,7 +63,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_NonDefaultCtor> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_NonDefaultCtor> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: }
//
@@ -74,7 +72,6 @@ struct HasDtor {
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } copy {
// CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}):
-// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: acc.yield
// CHECK-NEXT: } destroy {
// CHECK-NEXT: ^bb0(%[[ORIG:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}, %[[ARG:.*]]: !cir.ptr<!rec_HasDtor> {{.*}}):
@@ -177,7 +174,6 @@ struct HasDtor {
// CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NoCopyConstruct>, !u64i) -> !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>> -> !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NoCopyConstruct>, !u64i) -> !cir.ptr<!rec_NoCopyConstruct>
-// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] : !cir.ptr<!rec_NoCopyConstruct>
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr<!u64i>, !u64i
@@ -247,7 +243,6 @@ struct HasDtor {
// CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NonDefaultCtor>, !u64i) -> !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_NonDefaultCtor x 5>> -> !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr<!rec_NonDefaultCtor>, !u64i) -> !cir.ptr<!rec_NonDefaultCtor>
-// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] : !cir.ptr<!rec_NonDefaultCtor>
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr<!u64i>, !u64i
@@ -282,7 +277,6 @@ struct HasDtor {
// CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr<!rec_HasDtor>, !u64i) -> !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_HasDtor x 5>> -> !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr<!rec_HasDtor>, !u64i) -> !cir.ptr<!rec_HasDtor>
-// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] : !cir.ptr<!rec_HasDtor>
// CHECK-NEXT: cir.yield
// CHECK-NEXT: } step {
// CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr<!u64i>, !u64i
More information about the cfe-commits
mailing list