diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 1e0fb038b19d8..07a5b1f3a06c8 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -822,6 +822,120 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> { }]; } +//===----------------------------------------------------------------------===// +// CXX SpecialMemberAttr +//===----------------------------------------------------------------------===// + +def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [ + I32EnumAttrCase<"Custom", 0, "custom">, + I32EnumAttrCase<"Default", 1, "default">, + I32EnumAttrCase<"Copy", 2, "copy">, + I32EnumAttrCase<"Move", 3, "move">, +]> { + let genSpecializedAttr = 0; +} + + +def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> { + let summary = "Marks a function as a C++ constructor"; + let description = [{ + This attribute identifies a C++ constructor and classifies its kind: + + - `custom`: a user-defined constructor + - `default`: a default constructor + - `copy`: a copy constructor + - `move`: a move constructor + + Example: + ```mlir + #cir.cxx_ctor + #cir.cxx_ctor + ``` + }]; + + let parameters = (ins + "mlir::Type":$type, + EnumParameter:$ctor_kind, + DefaultValuedParameter<"bool", "false">:$is_trivial + ); + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type, + CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind, + CArg<"bool", "false">:$isTrivial), [{ + return $_get(type.getContext(), type, ctorKind, isTrivial); + }]>, + ]; + + let assemblyFormat = [{ + `<` $type `,` $ctor_kind (`,` `trivial` $is_trivial^)? `>` + }]; +} + +def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> { + let summary = "Marks a function as a CXX destructor"; + let description = [{ + This attribute identifies a C++ destructor. + }]; + + let parameters = (ins + "mlir::Type":$type, + DefaultValuedParameter<"bool", "false">:$is_trivial + ); + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type, + CArg<"bool", "false">:$isTrivial), [{ + return $_get(type.getContext(), type, isTrivial); + }]> + ]; + + let assemblyFormat = [{ + `<` $type (`,` `trivial` $is_trivial^)? `>` + }]; +} + +def CIR_AssignKind : CIR_I32EnumAttr<"AssignKind", "CXX Assignment Operator Kind", [ + I32EnumAttrCase<"Copy", 0, "copy">, + I32EnumAttrCase<"Move", 1, "move">, +]> { + let genSpecializedAttr = 0; +} + +def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", "cxx_assign"> { + let summary = "Marks a function as a CXX assignment operator"; + let description = [{ + This attribute identifies a C++ assignment operator and classifies its kind: + + - `copy`: a copy assignment + - `move`: a move assignment + }]; + + let parameters = (ins + "mlir::Type":$type, + EnumParameter:$assign_kind, + DefaultValuedParameter<"bool", "false">:$is_trivial + ); + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type, + CArg<"AssignKind">:$assignKind, + CArg<"bool", "false">:$isTrivial), [{ + return $_get(type.getContext(), type, assignKind, isTrivial); + }]> + ]; + + let assemblyFormat = [{ + `<` $type `,` $assign_kind (`,` `trivial` $is_trivial^)? `>` + }]; +} + +def CIR_CXXSpecialMemberAttr : AnyAttrOf<[ + CIR_CXXCtorAttr, + CIR_CXXDtorAttr, + CIR_CXXAssignAttr +]>; + //===----------------------------------------------------------------------===// // BitfieldInfoAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 2124b1dc62a81..3c7693979c403 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2533,7 +2533,9 @@ def CIR_FuncOp : CIR_Op<"func", [ OptionalAttr:$res_attrs, OptionalAttr:$aliasee, CIR_OptionalPriorityAttr:$global_ctor_priority, - CIR_OptionalPriorityAttr:$global_dtor_priority); + CIR_OptionalPriorityAttr:$global_dtor_priority, + OptionalAttr:$cxx_special_member + ); let regions = (region AnyRegion:$body); @@ -2572,7 +2574,33 @@ def CIR_FuncOp : CIR_Op<"func", [ //===------------------------------------------------------------------===// bool isDeclaration(); - }]; + + //===------------------------------------------------------------------===// + // C++ Special Member Functions + //===------------------------------------------------------------------===// + + /// Returns true if this function is a C++ special member function. + bool isCXXSpecialMemberFunction(); + + bool isCxxConstructor(); + + bool isCxxDestructor(); + + /// Returns true if this function is a copy or move assignment operator. + bool isCxxSpecialAssignment(); + + /// Returns the kind of constructor this function represents, if any. + std::optional getCxxConstructorKind(); + + /// Returns the kind of assignment operator (move, copy) this function + /// represents, if any. + std::optional getCxxSpecialAssignKind(); + + /// Returns true if the function is a trivial C++ member functions such as + /// trivial default constructor, copy/move constructor, copy/move assignment, + /// or destructor. + bool isCxxTrivialMemberFunction(); +}]; let hasCustomAssemblyFormat = 1; let hasVerifier = 1; diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index a8296782ebc40..7e6050012b09d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" using namespace clang; @@ -786,6 +787,8 @@ void CIRGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &args) { "Body of an implicit assignment operator should be compound stmt."); const auto *rootCS = cast(rootS); + cgm.setCXXSpecialMemberAttr(cast(curFn), assignOp); + assert(!cir::MissingFeatures::incrementProfileCounter()); assert(!cir::MissingFeatures::runCleanupsScope()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 866fda3166f41..be80df3091655 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -560,7 +560,7 @@ static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) { cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, cir::FuncType funcType) { - const auto funcDecl = cast(gd.getDecl()); + const auto *funcDecl = cast(gd.getDecl()); curGD = gd; if (funcDecl->isInlineBuiltinDeclaration()) { @@ -630,6 +630,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, { LexicalScope lexScope(*this, fusedLoc, entryBB); + // Emit the standard function prologue. startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin()); // Save parameters for coroutine function. @@ -656,6 +657,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, // copy-constructors. emitImplicitAssignmentOperatorBody(args); } else if (body) { + // Emit standard function body. if (mlir::failed(emitFunctionBody(body))) { return nullptr; } @@ -683,6 +685,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { ctorType == Ctor_Complete) && "can only generate complete ctor for this ABI"); + cgm.setCXXSpecialMemberAttr(cast(curFn), ctor); + if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) && cgm.getTarget().getCXXABI().hasConstructorVariants()) { emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc()); @@ -721,6 +725,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { const CXXDestructorDecl *dtor = cast(curGD.getDecl()); CXXDtorType dtorType = curGD.getDtorType(); + cgm.setCXXSpecialMemberAttr(cast(curFn), dtor); + // For an abstract class, non-base destructors are never used (and can't // be emitted in general, because vbase dtors may not have been validated // by Sema), but the Itanium ABI doesn't make them optional and Clang may diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index c1f2581eb96e3..3b9c5cfbb0243 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2211,6 +2211,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, assert(!cir::MissingFeatures::opFuncExtraAttrs()); + // Mark C++ special member functions (Constructor, Destructor etc.) + setCXXSpecialMemberAttr(func, funcDecl); + if (!cgf) theModule.push_back(func); } @@ -2226,6 +2229,58 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name, return fnOp; } +static cir::CtorKind getCtorKindFromDecl(const CXXConstructorDecl *ctor) { + if (ctor->isDefaultConstructor()) + return cir::CtorKind::Default; + if (ctor->isCopyConstructor()) + return cir::CtorKind::Copy; + if (ctor->isMoveConstructor()) + return cir::CtorKind::Move; + return cir::CtorKind::Custom; +} + +static cir::AssignKind getAssignKindFromDecl(const CXXMethodDecl *method) { + if (method->isCopyAssignmentOperator()) + return cir::AssignKind::Copy; + if (method->isMoveAssignmentOperator()) + return cir::AssignKind::Move; + llvm_unreachable("not a copy or move assignment operator"); +} + +void CIRGenModule::setCXXSpecialMemberAttr( + cir::FuncOp funcOp, const clang::FunctionDecl *funcDecl) { + if (!funcDecl) + return; + + if (const auto *dtor = dyn_cast(funcDecl)) { + auto cxxDtor = cir::CXXDtorAttr::get( + convertType(getASTContext().getCanonicalTagType(dtor->getParent())), + dtor->isTrivial()); + funcOp.setCxxSpecialMemberAttr(cxxDtor); + return; + } + + if (const auto *ctor = dyn_cast(funcDecl)) { + cir::CtorKind kind = getCtorKindFromDecl(ctor); + auto cxxCtor = cir::CXXCtorAttr::get( + convertType(getASTContext().getCanonicalTagType(ctor->getParent())), + kind, ctor->isTrivial()); + funcOp.setCxxSpecialMemberAttr(cxxCtor); + return; + } + + const auto *method = dyn_cast(funcDecl); + if (method && (method->isCopyAssignmentOperator() || + method->isMoveAssignmentOperator())) { + cir::AssignKind assignKind = getAssignKindFromDecl(method); + auto cxxAssign = cir::CXXAssignAttr::get( + convertType(getASTContext().getCanonicalTagType(method->getParent())), + assignKind, method->isTrivial()); + funcOp.setCxxSpecialMemberAttr(cxxAssign); + return; + } +} + cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty, StringRef name, mlir::ArrayAttr, [[maybe_unused]] bool isLocal, diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index dc28d9e8e9d33..3ac88c674d66e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -497,6 +497,10 @@ class CIRGenModule : public CIRGenTypeCache { cir::FuncType ty, const clang::FunctionDecl *fd); + /// Mark the function as a special member (e.g. constructor, destructor) + void setCXXSpecialMemberAttr(cir::FuncOp funcOp, + const clang::FunctionDecl *funcDecl); + cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name, mlir::ArrayAttr = {}, bool isLocal = false, bool assumeConvergent = false); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 9ac5efe0e41c7..26a7a6f2831dd 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -12,9 +12,11 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "mlir/IR/Attributes.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/FunctionImplementation.h" @@ -1658,6 +1660,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name); mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name); mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name); + mlir::StringAttr specialMemberAttr = getCxxSpecialMemberAttrName(state.name); if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref()))) state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr()); @@ -1756,6 +1759,23 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { return success(); }; + // Parse CXXSpecialMember attribute + if (parser.parseOptionalKeyword("special_member").succeeded()) { + cir::CXXCtorAttr ctorAttr; + cir::CXXDtorAttr dtorAttr; + cir::CXXAssignAttr assignAttr; + if (parser.parseLess().failed()) + return failure(); + if (parser.parseOptionalAttribute(ctorAttr).has_value()) + state.addAttribute(specialMemberAttr, ctorAttr); + else if (parser.parseOptionalAttribute(dtorAttr).has_value()) + state.addAttribute(specialMemberAttr, dtorAttr); + else if (parser.parseOptionalAttribute(assignAttr).has_value()) + state.addAttribute(specialMemberAttr, assignAttr); + if (parser.parseGreater().failed()) + return failure(); + } + if (parseGlobalDtorCtor("global_ctor", [&](std::optional priority) { mlir::IntegerAttr globalCtorPriorityAttr = builder.getI32IntegerAttr(priority.value_or(65535)); @@ -1833,6 +1853,56 @@ bool cir::FuncOp::isDeclaration() { return false; } +bool cir::FuncOp::isCXXSpecialMemberFunction() { + return getCxxSpecialMemberAttr() != nullptr; +} + +bool cir::FuncOp::isCxxConstructor() { + auto attr = getCxxSpecialMemberAttr(); + return attr && dyn_cast(attr); +} + +bool cir::FuncOp::isCxxDestructor() { + auto attr = getCxxSpecialMemberAttr(); + return attr && dyn_cast(attr); +} + +bool cir::FuncOp::isCxxSpecialAssignment() { + auto attr = getCxxSpecialMemberAttr(); + return attr && dyn_cast(attr); +} + +std::optional cir::FuncOp::getCxxConstructorKind() { + mlir::Attribute attr = getCxxSpecialMemberAttr(); + if (attr) { + if (auto ctor = dyn_cast(attr)) + return ctor.getCtorKind(); + } + return std::nullopt; +} + +std::optional cir::FuncOp::getCxxSpecialAssignKind() { + mlir::Attribute attr = getCxxSpecialMemberAttr(); + if (attr) { + if (auto assign = dyn_cast(attr)) + return assign.getAssignKind(); + } + return std::nullopt; +} + +bool cir::FuncOp::isCxxTrivialMemberFunction() { + mlir::Attribute attr = getCxxSpecialMemberAttr(); + if (attr) { + if (auto ctor = dyn_cast(attr)) + return ctor.getIsTrivial(); + if (auto dtor = dyn_cast(attr)) + return dtor.getIsTrivial(); + if (auto assign = dyn_cast(attr)) + return assign.getIsTrivial(); + } + return false; +} + mlir::Region *cir::FuncOp::getCallableRegion() { // TODO(CIR): This function will have special handling for aliases and a // check for an external function, once those features have been upstreamed. @@ -1883,6 +1953,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) { p << ")"; } + if (auto specialMemberAttr = getCxxSpecialMember()) { + p << " special_member<"; + p.printAttribute(*specialMemberAttr); + p << '>'; + } + if (auto globalCtorPriority = getGlobalCtorPriority()) { p << " global_ctor"; if (globalCtorPriority.value() != 65535) diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 29b1211d2c351..5aa482a661ba5 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -8,10 +8,12 @@ #include "LoweringPrepareCXXABI.h" #include "PassDetail.h" +#include "mlir/IR/Attributes.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/Module.h" #include "clang/Basic/TargetInfo.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "clang/CIR/Dialect/Passes.h" @@ -72,6 +74,7 @@ struct LoweringPreparePass void lowerDynamicCastOp(cir::DynamicCastOp op); void lowerArrayDtor(cir::ArrayDtor op); void lowerArrayCtor(cir::ArrayCtor op); + void lowerTrivialCopyCall(cir::CallOp op); /// Build the function that initializes the specified global cir::FuncOp buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op); @@ -984,6 +987,28 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) { true); } +void LoweringPreparePass::lowerTrivialCopyCall(cir::CallOp op) { + FuncOp funcOp = getCalledFunction(op); + if (!funcOp) + return; + + llvm::errs() << "Lower trivial copy call: " << funcOp.getName() << "\n"; + + std::optional ctorKind = funcOp.getCxxConstructorKind(); + if (ctorKind && *ctorKind == cir::CtorKind::Copy + && funcOp.isCxxTrivialMemberFunction()) { + llvm::outs() << "success \n"; + // Replace the trivial copy constructor call with a `CopyOp` + CIRBaseBuilderTy builder(getContext()); + auto operands = op.getOperands(); + mlir::Value dest = operands[0]; + mlir::Value src = operands[1]; + builder.setInsertionPoint(op); + builder.createCopy(dest, src); + op.erase(); + } +} + void LoweringPreparePass::runOnOp(mlir::Operation *op) { if (auto arrayCtor = dyn_cast(op)) { lowerArrayCtor(arrayCtor); @@ -1001,6 +1026,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) { lowerDynamicCastOp(dynamicCast); } else if (auto unary = mlir::dyn_cast(op)) { lowerUnaryOp(unary); + } else if (auto callOp = dyn_cast(op)) { + lowerTrivialCopyCall(callOp); } else if (auto fnOp = dyn_cast(op)) { if (auto globalCtor = fnOp.getGlobalCtorPriority()) globalCtorList.emplace_back(fnOp.getName(), globalCtor.value()); @@ -1019,7 +1046,7 @@ void LoweringPreparePass::runOnOperation() { op->walk([&](mlir::Operation *op) { if (mlir::isa(op)) + cir::FuncOp, cir::CallOp, cir::GlobalOp, cir::UnaryOp>(op)) opsToTransform.push_back(op); }); diff --git a/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp new file mode 100644 index 0000000000000..cd1377ac04eac --- /dev/null +++ b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -std=c++11 -triple aarch64-none-linux-android21 -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s + +struct Flub { + int a = 123; + // COM: Trivial copy constructors/assignments are replaced with cir.copy + // CIR: cir.copy {{.*}} : !cir.ptr + // CIR: @_ZN4FlubC1EOS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) special_member<#cir.cxx_ctor + // CIR: @_ZN4FlubaSERKS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) -> !cir.ptr special_member<#cir.cxx_assign> + // CIR: @_ZN4FlubaSEOS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) -> !cir.ptr special_member<#cir.cxx_assign> +}; + +struct Foo { + int a; + + // CIR: @_ZN3FooC2Ev(%arg0: !cir.ptr loc({{.*}})) special_member<#cir.cxx_ctor> + Foo() : a(123) {} + + // CIR: @_ZN3FooC2ERKS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) special_member<#cir.cxx_ctor> + Foo(const Foo &other) : a(other.a) {} + + // CIR: @_ZN3FooC2EOS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) special_member<#cir.cxx_ctor> + Foo(Foo &&other) noexcept : a(other.a) { other.a = 0; } + + // CIR: @_ZN3FooaSERKS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) -> !cir.ptr special_member<#cir.cxx_assign> + Foo &operator=(const Foo &other) { + if (this != &other) { + a = other.a; + } + return *this; + } + + // CIR: @_ZN3FooaSEOS_(%arg0: !cir.ptr loc({{.*}}), %arg1: !cir.ptr loc({{.*}})) -> !cir.ptr special_member<#cir.cxx_assign> + Foo &operator=(Foo &&other) noexcept { + if (this != &other) { + a = other.a; + other.a = 0; + } + return *this; + } + + // CIR: @_ZN3FooD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> + ~Foo(); +}; + +void trivial_func() { + Flub f1{}; + Flub f2 = f1; + Flub f3 = static_cast(f1); + f2 = f1; + f1 = static_cast(f3); +} + +void non_trivial_func() { + Foo f1{}; + Foo f2 = f1; + Foo f3 = static_cast(f1); + f2 = f1; + f1 = static_cast(f3); +} diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp index c15e7e7c57b9f..969ab46339d62 100644 --- a/clang/test/CIR/CodeGen/struct.cpp +++ b/clang/test/CIR/CodeGen/struct.cpp @@ -109,7 +109,7 @@ void paren_expr() { // CIR: %[[B_ADDR:.*]] = cir.alloca !rec_Point, !cir.ptr, ["b", init] // CIR: %[[CONST:.*]] = cir.const #cir.zero : !rec_Point // CIR: cir.store{{.*}} %[[CONST]], %[[A_ADDR]] : !rec_Point, !cir.ptr -// CIR: cir.call @_ZZ10paren_exprvEN5PointC1ERKS_(%[[B_ADDR]], %[[A_ADDR]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CIR: cir.copy %[[A_ADDR]] to %[[B_ADDR]]) -> !cir.ptr // LLVM: define{{.*}} void @_Z10paren_exprv() // LLVM: %[[A_ADDR:.*]] = alloca %struct.Point, i64 1, align 4 diff --git a/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp b/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp index 94f3f1ace4350..3cd8209d05076 100644 --- a/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp +++ b/clang/test/CIR/CodeGenOpenACC/combined-firstprivate-clause.cpp @@ -43,7 +43,7 @@ struct HasDtor { // CHECK-NEXT: acc.yield // CHECK-NEXT: } copy { // CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr {{.*}}, %[[ARG_TO:.*]]: !cir.ptr {{.*}}): -// CHECK-NEXT: cir.call @_ZN15NoCopyConstructC1ERKS_(%[[ARG_TO]], %[[ARG_FROM]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy %[[ARG_FROM]] TO %[[ARG_FROM]] -> !cir.ptr // CHECK-NEXT: acc.yield // CHECK-NEXT: } // @@ -176,7 +176,7 @@ struct HasDtor { // CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr, !u64i) -> !cir.ptr // CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr> -> !cir.ptr // CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr, !u64i) -> !cir.ptr -// CHECK-NEXT: cir.call @_ZN15NoCopyConstructC1ERKS_(%[[STRIDE_TO]], %[[STRIDE_FROM]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]] -> !cir.ptr // CHECK-NEXT: cir.yield // CHECK-NEXT: } step { // CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr, !u64i diff --git a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp index ed968e21630cc..fb424f65f28cd 100644 --- a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp +++ b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause-templates.cpp @@ -30,7 +30,7 @@ struct HasDtor { // CHECK-NEXT: acc.yield // CHECK-NEXT: } copy { // CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr {{.*}}, %[[ARG_TO:.*]]: !cir.ptr {{.*}}): -// CHECK-NEXT: cir.call @_ZN14NonDefaultCtorC1ERKS_(%[[ARG_TO]], %[[ARG_FROM]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]]) -> !cir.ptr // CHECK-NEXT: acc.yield // CHECK-NEXT: } // diff --git a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp index a2c6c3834b1e2..ecc7a49cf8c62 100644 --- a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp +++ b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.cpp @@ -43,7 +43,7 @@ struct HasDtor { // CHECK-NEXT: acc.yield // CHECK-NEXT: } copy { // CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr {{.*}}, %[[ARG_TO:.*]]: !cir.ptr {{.*}}): -// CHECK-NEXT: cir.call @_ZN15NoCopyConstructC1ERKS_(%[[ARG_TO]], %[[ARG_FROM]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr // CHECK-NEXT: acc.yield // CHECK-NEXT: } // @@ -63,7 +63,7 @@ struct HasDtor { // CHECK-NEXT: acc.yield // CHECK-NEXT: } copy { // CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr {{.*}}, %[[ARG_TO:.*]]: !cir.ptr {{.*}}): -// CHECK-NEXT: cir.call @_ZN14NonDefaultCtorC1ERKS_(%[[ARG_TO]], %[[ARG_FROM]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]]) -> cir.ptr // CHECK-NEXT: acc.yield // CHECK-NEXT: } // @@ -176,7 +176,7 @@ struct HasDtor { // CHECK-NEXT: %[[STRIDE_FROM:.*]] = cir.ptr_stride %[[DECAY_FROM]], %[[ITR_LOAD]] : (!cir.ptr, !u64i) -> !cir.ptr // CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast array_to_ptrdecay %[[ARG_TO]] : !cir.ptr> -> !cir.ptr // CHECK-NEXT: %[[STRIDE_TO:.*]] = cir.ptr_stride %[[DECAY_TO]], %[[ITR_LOAD]] : (!cir.ptr, !u64i) -> !cir.ptr -// CHECK-NEXT: cir.call @_ZN15NoCopyConstructC1ERKS_(%[[STRIDE_TO]], %[[STRIDE_FROM]]) nothrow : (!cir.ptr, !cir.ptr) -> () +// CHECK-NEXT: cir.copy %[[STRIDE_FROM]] to %[[STRIDE_TO]]) -> !cir.ptr // CHECK-NEXT: cir.yield // CHECK-NEXT: } step { // CHECK-NEXT: %[[ITR_LOAD]] = cir.load %[[ITR]] : !cir.ptr, !u64i diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir index 6e91898a3b452..1f25e98a1eee0 100644 --- a/clang/test/CIR/IR/func.cir +++ b/clang/test/CIR/IR/func.cir @@ -143,3 +143,37 @@ cir.func @global_dtor_with_priority() global_dtor(201) { // CHECK: } } + +!rec_Foo = !cir.record + +cir.func @Foo_default() special_member<#cir.cxx_ctor> { + cir.return +} + +// CHECK: cir.func @Foo_default() special_member<#cir.cxx_ctor> { +// CHECK: cir.return +// CHECK: } + +cir.func @Foo_trivial_copy() special_member<#cir.cxx_ctor> { + cir.return +} + +// CHECK: cir.func @Foo_trivial_copy() special_member<#cir.cxx_ctor> { +// CHECK: cir.return +// CHECK: } + +cir.func @Foo_destructor() special_member<#cir.cxx_dtor> { + cir.return +} + +// CHECK: cir.func @Foo_destructor() special_member<#cir.cxx_dtor> { +// CHECK: cir.return +// CHECK: } + +cir.func @Foo_move_assign() special_member<#cir.cxx_assign> { + cir.return +} + +// CHECK: cir.func @Foo_move_assign() special_member<#cir.cxx_assign> { +// CHECK: cir.return +// CHECK: }