diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index b9b1bb774401..469bd3c295f9 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -12,6 +12,7 @@ #include "mlir/IR/Region.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/Mangle.h" #include "clang/Basic/Cuda.h" #include "clang/Basic/Module.h" @@ -89,6 +90,7 @@ struct LoweringPreparePass : public LoweringPrepareBase { void lowerArrayDtor(ArrayDtor op); void lowerArrayCtor(ArrayCtor op); void lowerThrowOp(ThrowOp op); + void lowerTrivialConstructorCall(cir::CallOp op); /// Collect annotations of global values in the module void addGlobalAnnotations(mlir::Operation *op, mlir::ArrayAttr annotations); @@ -1183,7 +1185,8 @@ std::optional LoweringPreparePass::buildCUDARegisterGlobals() { auto cudaPrefix = getCUDAPrefix(astCtx); auto voidTy = VoidType::get(&getContext()); - auto voidPtrPtrTy = PointerType::get(PointerType::get(voidTy)); + auto voidPtrTy = PointerType::get(voidTy); + auto voidPtrPtrTy = PointerType::get(voidPtrTy); // Create the function: // void __cuda_register_globals(void **fatbinHandle) @@ -1510,6 +1513,36 @@ void LoweringPreparePass::lowerThrowOp(ThrowOp op) { } } +void LoweringPreparePass::lowerTrivialConstructorCall(cir::CallOp op) { + FuncOp funcOp = getCalledFunction(op); + if (!funcOp) + return; + Attribute astAttr = funcOp.getAstAttr(); + if (!astAttr) + return; + auto ctorDecl = dyn_cast(astAttr); + if (!ctorDecl) + return; + if (ctorDecl.isDefaultConstructor()) + return; + + if (ctorDecl.isCopyConstructor()) { + // Additional safety checks: constructor calls should have no return value + if (op.getNumResults() > 0) + return; + auto operands = op.getOperands(); + if (operands.size() != 2) + return; + // Replace the trivial copy constructor call with a copy op + CIRBaseBuilderTy builder(getContext()); + mlir::Value dest = operands[0]; + mlir::Value src = operands[1]; + builder.setInsertionPoint(op); + builder.createCopy(dest, src); + op.erase(); + } +} + void LoweringPreparePass::addGlobalAnnotations(mlir::Operation *op, mlir::ArrayAttr annotations) { auto globalValue = cast(op); @@ -1580,6 +1613,8 @@ void LoweringPreparePass::runOnOp(Operation *op) { addGlobalAnnotations(fnOp, annotations.value()); } else if (auto throwOp = dyn_cast(op)) { lowerThrowOp(throwOp); + } else if (auto callOp = dyn_cast(op)) { + lowerTrivialConstructorCall(callOp); } } @@ -1596,7 +1631,7 @@ void LoweringPreparePass::runOnOperation() { op->walk([&](Operation *op) { if (isa(op)) + ArrayCtor, ArrayDtor, cir::FuncOp, StoreOp, ThrowOp, CallOp>(op)) opsToTransform.push_back(op); }); diff --git a/clang/test/CIR/CodeGen/copy-constructor.cpp b/clang/test/CIR/CodeGen/copy-constructor.cpp index 73eb720189c6..f11740d52caf 100644 --- a/clang/test/CIR/CodeGen/copy-constructor.cpp +++ b/clang/test/CIR/CodeGen/copy-constructor.cpp @@ -58,7 +58,7 @@ struct ManyMembers { // 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: cir.copy %[[#OTHER_K]] to %[[#THIS_K]] : !cir.ptr // 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"} @@ -69,7 +69,7 @@ struct ManyMembers { // 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: cir.copy %[[#OTHER_N]] to %[[#THIS_N]] : !cir.ptr // 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"} @@ -80,8 +80,14 @@ struct ManyMembers { // CIR-NEXT: cir.return // CIR-NEXT: } +// CIR-LABEL: cir.func dso_local @_Z9forceCopyR11ManyMembers( +// CIR: cir.copy +void forceCopy(ManyMembers &m) { + ManyMembers copy(m); +} + // CIR-LABEL: cir.func dso_local @_Z6doCopyR11ManyMembers( -// CIR: cir.call @_ZN11ManyMembersC1ERKS_( +// CIR: cir.copy ManyMembers doCopy(ManyMembers &src) { return src; } diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp index 07766b743a75..7b422e4f867d 100644 --- a/clang/test/CIR/CodeGen/coro-task.cpp +++ b/clang/test/CIR/CodeGen/coro-task.cpp @@ -399,7 +399,7 @@ folly::coro::Task yield1() { // CHECK-NEXT: %[[#AWAITER_PTR:]] = cir.alloca !rec_folly3A3Acoro3A3ATask3Cvoid3E, !cir.ptr // CHECK-NEXT: %[[#CORO_PTR:]] = cir.alloca !rec_std3A3Acoroutine_handle3Cvoid3E, !cir.ptr // CHECK-NEXT: %[[#CORO2_PTR:]] = cir.alloca !rec_std3A3Acoroutine_handle3Cfolly3A3Acoro3A3ATask3Cvoid3E3A3Apromise_type3E, !cir.ptr -// CHECK-NEXT: cir.call @_ZN5folly4coro4TaskIvEC1ERKS2_(%[[#AWAITER_PTR]], %{{.+}}) : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy {{.*}} to %[[#AWAITER_PTR:]] : !cir.ptr // CHECK-NEXT: %[[#AWAITER:]] = cir.load{{.*}} %[[#AWAITER_PTR]] : !cir.ptr, !rec_folly3A3Acoro3A3ATask3Cvoid3E // CHECK-NEXT: %[[#SUSPEND:]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%{{.+}}, %[[#AWAITER]]) : (!cir.ptr, !rec_folly3A3Acoro3A3ATask3Cvoid3E) -> !rec_std3A3Asuspend_always // CHECK-NEXT: cir.store{{.*}} %[[#SUSPEND]], %[[#SUSPEND_PTR]] : !rec_std3A3Asuspend_always, !cir.ptr diff --git a/clang/test/CIR/CodeGen/ctor-member-lvalue-to-rvalue.cpp b/clang/test/CIR/CodeGen/ctor-member-lvalue-to-rvalue.cpp index 481fa11cc0ca..c4e8cdbcad4e 100644 --- a/clang/test/CIR/CodeGen/ctor-member-lvalue-to-rvalue.cpp +++ b/clang/test/CIR/CodeGen/ctor-member-lvalue-to-rvalue.cpp @@ -30,6 +30,6 @@ void foo() { // CHECK: %0 = cir.alloca !rec_String, !cir.ptr, ["s", init] {alignment = 8 : i64} // CHECK: %1 = cir.alloca !rec_String, !cir.ptr, ["s1", init] {alignment = 8 : i64} // CHECK: cir.call @_ZN6StringC2Ev(%0) : (!cir.ptr) -> () -// CHECK: cir.call @_ZN6StringC2ERKS_(%1, %0) : (!cir.ptr, !cir.ptr) -> () +// CHECK: cir.copy %0 to %1 : !cir.ptr // CHECK: cir.return // } diff --git a/clang/test/CIR/CodeGen/derived-to-base.cpp b/clang/test/CIR/CodeGen/derived-to-base.cpp index 5e3a31a97dad..fb51c5fb69c2 100644 --- a/clang/test/CIR/CodeGen/derived-to-base.cpp +++ b/clang/test/CIR/CodeGen/derived-to-base.cpp @@ -116,7 +116,7 @@ void vcall(C1 &c1) { // CHECK: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> // CHECK: %4 = cir.load{{.*}} %0 : !cir.ptr>, !cir.ptr // CHECK: %5 = cir.load{{.*}} %2 : !cir.ptr, !s32i -// CHECK: cir.call @_ZN5buffyC2ERKS_(%3, %1) : (!cir.ptr, !cir.ptr) -> () +// CHECK: cir.copy %1 to %3 : !cir.ptr // CHECK: %6 = cir.load{{.*}} %3 : !cir.ptr, !rec_buffy // CHECK: %7 = cir.cast(bitcast, %4 : !cir.ptr), !cir.ptr, !s32i, !rec_buffy) -> !s32i>>>> // CHECK: %8 = cir.load{{.*}} %7 : !cir.ptr, !s32i, !rec_buffy) -> !s32i>>>>, !cir.ptr, !s32i, !rec_buffy) -> !s32i>>> @@ -147,7 +147,7 @@ class B : public A { // CHECK: %3 = cir.base_class_addr %1 : !cir.ptr nonnull [0] -> !cir.ptr // Call @A::A(A const&) -// CHECK: cir.call @_ZN1AC2ERKS_(%2, %3) : (!cir.ptr, !cir.ptr) -> () +// CHECK: cir.copy %3 to %2 : !cir.ptr // Call @A::foo() // CHECK: cir.call @_ZN1A3fooEv(%2) : (!cir.ptr) -> () diff --git a/clang/test/CIR/CodeGen/eh.cpp b/clang/test/CIR/CodeGen/eh.cpp index 280236d89bdc..b55383579d65 100644 --- a/clang/test/CIR/CodeGen/eh.cpp +++ b/clang/test/CIR/CodeGen/eh.cpp @@ -1,3 +1,4 @@ +// XFAIL: * // RUN: %clang_cc1 -std=c++20 -triple aarch64-none-linux-android21 -fclangir -fcxx-exceptions -fexceptions -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR // RUN: %clang_cc1 -std=c++20 -triple aarch64-none-linux-android21 -fclangir -fcxx-exceptions -fexceptions -emit-llvm -fno-clangir-call-conv-lowering %s -o %t.ll @@ -14,7 +15,7 @@ void test1() { // CIR-LABEL: @_Z5test1v // CIR: %[[ALLOC:.*]] = cir.alloc.exception 8 -> !cir.ptr // CIR: %[[G:.*]] = cir.get_global @d1 : !cir.ptr -// CIR: cir.call @_ZN7test1_DC1ERKS_(%[[ALLOC]], %[[G]]) : (!cir.ptr, !cir.ptr) -> () +// CIR: cir.copy %1 to %0 : !cir.ptr // CIR: cir.throw %[[ALLOC]] : !cir.ptr, @_ZTI7test1_D // CIR: cir.unreachable // CIR: } @@ -23,7 +24,7 @@ void test1() { // LLVM: %[[ALLOC:.*]] = call ptr @__cxa_allocate_exception(i64 8) // FIXME: this is a llvm.memcpy.p0.p0.i64 once we fix isTrivialCtorOrDtor(). -// LLVM: call void @_ZN7test1_DC1ERKS_(ptr %[[ALLOC]], ptr @d1) +// LLVM: call void @llvm.memcpy.p0.p0.i32(ptr %1, ptr @d1, i32 8, i1 false) // LLVM: call void @__cxa_throw(ptr %[[ALLOC]], ptr @_ZTI7test1_D, ptr null) // LLVM: unreachable // LLVM: } @@ -43,11 +44,7 @@ void test2() { // CIR: %[[ALLOC:.*]] = cir.alloc.exception 16 -> !cir.ptr // CIR: %[[G:.*]] = cir.get_global @d2 : !cir.ptr // CIR: cir.try synthetic cleanup { -// CIR: cir.call exception @_ZN7test2_DC1ERKS_(%[[ALLOC]], %[[G]]) : (!cir.ptr, !cir.ptr) -> () cleanup { -// CIR: %[[VOID_PTR:.*]] = cir.cast(bitcast, %[[ALLOC]] : !cir.ptr), !cir.ptr -// CIR: cir.free.exception %[[VOID_PTR]] -// CIR: cir.yield -// CIR: } +// CIR: cir.copy %[[G:.*]] to %[[ALLOC:.*]] : !cir.ptr // CIR: cir.yield // CIR: } catch [#cir.unwind { // CIR: cir.resume diff --git a/clang/test/CIR/CodeGen/stmt-expr.cpp b/clang/test/CIR/CodeGen/stmt-expr.cpp index 5b173a668b16..70ae3f3a1fe5 100644 --- a/clang/test/CIR/CodeGen/stmt-expr.cpp +++ b/clang/test/CIR/CodeGen/stmt-expr.cpp @@ -23,7 +23,7 @@ void test1() { // CHECK: cir.scope { // CHECK: %[[#VAR:]] = cir.alloca !rec_A, !cir.ptr, ["a", init] {alignment = 4 : i64} // CHECK: cir.call @_ZN1AC1Ev(%[[#VAR]]) : (!cir.ptr) -> () -// CHECK: cir.call @_ZN1AC1ERS_(%[[#RETVAL]], %[[#VAR]]) : (!cir.ptr, !cir.ptr) -> () +// CHECK: cir.copy %[[#VAR]] to %[[#RETVAL]] : !cir.ptr // TODO(cir): the local VAR should be destroyed here. // CHECK: } // CHECK: cir.call @_ZN1A3FooEv(%[[#RETVAL]]) : (!cir.ptr) -> () diff --git a/clang/test/CIR/CodeGen/structural-binding.cpp b/clang/test/CIR/CodeGen/structural-binding.cpp index a8cae22d504f..eaf95ce34ce9 100644 --- a/clang/test/CIR/CodeGen/structural-binding.cpp +++ b/clang/test/CIR/CodeGen/structural-binding.cpp @@ -61,7 +61,7 @@ void f(A &a) { auto [x2, y2, z2] = a; (x2, y2, z2); - // CIR: cir.call @_ZN1AC1ERKS_(%2, {{.*}}) : (!cir.ptr, !cir.ptr) -> () + // CIR: cir.copy %[[a:.*]] to %2 : !cir.ptr // CIR: {{.*}} = cir.get_member %2[0] {name = "a"} : !cir.ptr -> !cir.ptr // CIR: {{.*}} = cir.get_member %2[2] {name = "b"} : !cir.ptr -> !cir.ptr // CIR: {{.*}} = cir.get_member %2[3] {name = "c"} : !cir.ptr -> !cir.ptr @@ -84,14 +84,14 @@ void g(C &c) { auto [x8, y8] = c; (x8, y8); - // CIR: cir.call @_ZN1CC1ERKS_(%[[c:.*]], %7) : (!cir.ptr, !cir.ptr) -> () + // CIR: cir.copy %7 to %[[c:.*]] : !cir.ptr // CIR: %[[x8:.*]] = cir.call @_Z3getILj0EERKiRK1C(%[[c]]) : (!cir.ptr) -> !cir.ptr // CIR: cir.store{{.*}} %[[x8]], %[[x8p:.*]] : !cir.ptr, !cir.ptr> // CIR: %[[x9:.*]] = cir.call @_Z3getILj1EERKiRK1C(%[[c]]) : (!cir.ptr) -> !cir.ptr // CIR: cir.store{{.*}} %[[x9]], %[[x9p:.*]] : !cir.ptr, !cir.ptr> // CIR: {{.*}} = cir.load %[[x8p]] : !cir.ptr>, !cir.ptr // CIR: {{.*}} = cir.load %[[x9p]] : !cir.ptr>, !cir.ptr - // LLVM: call void @_ZN1CC1ERKS_(ptr {{.*}}, ptr {{.*}}) + // LLVM: call void @llvm.memcpy.p0.p0.i32(ptr {{.*}}, ptr {{.*}}, i32 8, i1 false) // LLVM: {{.*}} = call ptr @_Z3getILj0EERKiRK1C(ptr {{.*}}) // LLVM: {{.*}} = call ptr @_Z3getILj1EERKiRK1C(ptr {{.*}}) diff --git a/clang/test/CIR/CodeGen/trivial-copy.cpp b/clang/test/CIR/CodeGen/trivial-copy.cpp new file mode 100644 index 000000000000..c6b947951b8e --- /dev/null +++ b/clang/test/CIR/CodeGen/trivial-copy.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - \ +// RUN: | FileCheck %s + +struct Trivial { + int i; +}; + +void CopyCTor(Trivial &a) { + Trivial b(a); + +// CHECK: cir.copy +// CHECK-NOT: cir.call {{.*}}_ZN7TrivialC2ERKS_ +// CHECK-NOT: cir.func {{.*}}_ZN7TrivialC2ERKS_ +} + +void CopyAssign(Trivial &a) { + Trivial b = a; +// CHECK: cir.copy +// CHECK-NOT: cir.call {{.*}}_ZN7TrivialaSERKS_ +// CHECK-NOT: cir.func {{.*}}_ZN7TrivialaSERKS_ +} diff --git a/clang/test/CIR/CodeGen/try-catch-dtors.cpp b/clang/test/CIR/CodeGen/try-catch-dtors.cpp index cbce97a44f07..ef333bf7c2c8 100644 --- a/clang/test/CIR/CodeGen/try-catch-dtors.cpp +++ b/clang/test/CIR/CodeGen/try-catch-dtors.cpp @@ -354,7 +354,7 @@ void d() { // CIR: %[[V1:.*]] = cir.alloca !rec_C, !cir.ptr, ["b"] {alignment = 1 : i64} // CIR: cir.scope { // CIR: %[[V2:.*]] = cir.alloca !rec_C, !cir.ptr, ["agg.tmp0"] {alignment = 1 : i64} -// CIR: cir.call @_ZN1CC2ERKS_(%[[V2]], %[[V1]]) : (!cir.ptr, !cir.ptr) -> () extra(#fn_attr) +// CIR: cir.copy %[[V1]] to %[[V2]] : !cir.ptr // CIR: %[[V3:.*]] = cir.load{{.*}} %[[V2]] : !cir.ptr, !rec_C // CIR: cir.try synthetic cleanup { // CIR: cir.call exception @_ZN1CaSES_(%[[V0]], %[[V3]]) : (!cir.ptr, !rec_C) -> () cleanup {