Author: Hendrik Hübner Date: 2025-11-19T15:57:34-08:00 New Revision: 03f4d4d492b3f5e68645eac4e907f3f0fc7a4489
URL: https://github.com/llvm/llvm-project/commit/03f4d4d492b3f5e68645eac4e907f3f0fc7a4489 DIFF: https://github.com/llvm/llvm-project/commit/03f4d4d492b3f5e68645eac4e907f3f0fc7a4489.diff LOG: [CIR] Add CxxCTorAttr, CxxDTorAttr, CxxAssignAttr, CxxSpecialMemberAttr to cir::FuncOp (#167975) This PR adds a special member attribute to `cir::FuncOp`. This attribute is also present in the incubator repo. Additionally, I added a "is_trivial" flag, to mark trivial members. I think that might be useful when trying to replace calls to the copy constructor with memcpy for example, but please let me know your thoughts on this. [Here in the incubator repo](https://github.com/llvm/clangir/blob/823e943d1b9aaba0fc46f880c5a6ac8c29fc761d/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp#L1537-L1550) this function is called `LowerTrivialConstructorCall`, but I don't see a check that ensures the constructor is actually trivial. Added: clang/test/CIR/CodeGen/cxx-special-member-attr.cpp Modified: clang/include/clang/CIR/Dialect/IR/CIRAttrs.td clang/include/clang/CIR/Dialect/IR/CIROps.td clang/lib/CIR/CodeGen/CIRGenClass.cpp clang/lib/CIR/CodeGen/CIRGenFunction.cpp clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/CodeGen/CIRGenModule.h clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp clang/test/CIR/IR/func.cir Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 47ff9389e8028..12bc9cf7b5b04 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -822,6 +822,119 @@ 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<!rec_a, copy> + #cir.cxx_ctor<!rec_b, default, trivial> + ``` + }]; + + let parameters = (ins + "mlir::Type":$type, + EnumParameter<CIR_CtorKind>:$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<CIR_AssignKind>:$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 79a1b292df462..3c59a0b2a3144 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<DictArrayAttr>:$res_attrs, OptionalAttr<FlatSymbolRefAttr>:$aliasee, CIR_OptionalPriorityAttr:$global_ctor_priority, - CIR_OptionalPriorityAttr:$global_dtor_priority); + CIR_OptionalPriorityAttr:$global_dtor_priority, + OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member + ); let regions = (region AnyRegion:$body); @@ -2572,7 +2574,32 @@ 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<CtorKind> getCxxConstructorKind(); + + /// Returns the kind of assignment operator (move, copy) this function + /// represents, if any. + std::optional<AssignKind> 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; @@ -4362,7 +4389,7 @@ def CIR_ObjSizeOp : CIR_Op<"objsize", [Pure]> { When the `min` attribute is present, the operation returns the minimum guaranteed accessible size. When absent (max mode), it returns the maximum possible object size. Corresponds to `llvm.objectsize`'s `min` argument. - + The `dynamic` attribute determines if the value should be evaluated at runtime. Corresponds to `llvm.objectsize`'s `dynamic` argument. diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 89c4696b9da94..0f10347944fae 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<CompoundStmt>(rootS); + cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(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 2739469d7202e..7df650984bdd2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -570,7 +570,7 @@ static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) { cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, cir::FuncType funcType) { - const auto funcDecl = cast<FunctionDecl>(gd.getDecl()); + const auto *funcDecl = cast<FunctionDecl>(gd.getDecl()); curGD = gd; if (funcDecl->isInlineBuiltinDeclaration()) { @@ -640,6 +640,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. @@ -666,6 +667,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; } @@ -693,6 +695,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { ctorType == Ctor_Complete) && "can only generate complete ctor for this ABI"); + cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), ctor); + if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) && cgm.getTarget().getCXXABI().hasConstructorVariants()) { emitDelegateCXXConstructorCall(ctor, Ctor_Base, args, ctor->getEndLoc()); @@ -731,6 +735,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl()); CXXDtorType dtorType = curGD.getDtorType(); + cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(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 b8e51f87d4045..e09d3de5aac9a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2225,6 +2225,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); } @@ -2240,6 +2243,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<CXXDestructorDecl>(funcDecl)) { + auto cxxDtor = cir::CXXDtorAttr::get( + convertType(getASTContext().getCanonicalTagType(dtor->getParent())), + dtor->isTrivial()); + funcOp.setCxxSpecialMemberAttr(cxxDtor); + return; + } + + if (const auto *ctor = dyn_cast<CXXConstructorDecl>(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<CXXMethodDecl>(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 7601e39a798d9..2c45bb238f95a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -503,6 +503,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 b5840ff12438e..6bf543cf794b7 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" @@ -1660,6 +1662,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()); @@ -1758,6 +1761,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<int> priority) { mlir::IntegerAttr globalCtorPriorityAttr = builder.getI32IntegerAttr(priority.value_or(65535)); @@ -1835,6 +1855,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<CXXCtorAttr>(attr); +} + +bool cir::FuncOp::isCxxDestructor() { + auto attr = getCxxSpecialMemberAttr(); + return attr && dyn_cast<CXXDtorAttr>(attr); +} + +bool cir::FuncOp::isCxxSpecialAssignment() { + auto attr = getCxxSpecialMemberAttr(); + return attr && dyn_cast<CXXAssignAttr>(attr); +} + +std::optional<CtorKind> cir::FuncOp::getCxxConstructorKind() { + mlir::Attribute attr = getCxxSpecialMemberAttr(); + if (attr) { + if (auto ctor = dyn_cast<CXXCtorAttr>(attr)) + return ctor.getCtorKind(); + } + return std::nullopt; +} + +std::optional<AssignKind> cir::FuncOp::getCxxSpecialAssignKind() { + mlir::Attribute attr = getCxxSpecialMemberAttr(); + if (attr) { + if (auto assign = dyn_cast<CXXAssignAttr>(attr)) + return assign.getAssignKind(); + } + return std::nullopt; +} + +bool cir::FuncOp::isCxxTrivialMemberFunction() { + mlir::Attribute attr = getCxxSpecialMemberAttr(); + if (attr) { + if (auto ctor = dyn_cast<CXXCtorAttr>(attr)) + return ctor.getIsTrivial(); + if (auto dtor = dyn_cast<CXXDtorAttr>(attr)) + return dtor.getIsTrivial(); + if (auto assign = dyn_cast<CXXAssignAttr>(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. @@ -1885,6 +1955,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..96a03ec3b8e9f 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" 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..815ef2c2aaa25 --- /dev/null +++ b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp @@ -0,0 +1,59 @@ +// 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; + // CIR: @_ZN4FlubC1ERKS_(%arg0: !cir.ptr<!rec_Flub> loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Flub, copy, trivial true>> + // CIR: @_ZN4FlubC2EOS_(%arg0: !cir.ptr<!rec_Flub> loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Flub, move, trivial true> + // 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>> +}; + +struct Foo { + int a; + + // CIR: @_ZN3FooC2Ev(%arg0: !cir.ptr<!rec_Foo> loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Foo, default>> + Foo() : a(123) {} + + // CIR: @_ZN3FooC2ERKS_(%arg0: !cir.ptr<!rec_Foo> loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Foo, copy>> + Foo(const Foo &other) : a(other.a) {} + + // CIR: @_ZN3FooC2EOS_(%arg0: !cir.ptr<!rec_Foo> loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Foo, move>> + Foo(Foo &&other) noexcept : a(other.a) { other.a = 0; } + + // CIR: @_ZN3FooaSERKS_(%arg0: !cir.ptr<!rec_Foo> loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> loc({{.*}})) -> !cir.ptr<!rec_Foo> special_member<#cir.cxx_assign<!rec_Foo, copy>> + Foo &operator=(const Foo &other) { + if (this != &other) { + a = other.a; + } + return *this; + } + + // CIR: @_ZN3FooaSEOS_(%arg0: !cir.ptr<!rec_Foo> loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> loc({{.*}})) -> !cir.ptr<!rec_Foo> special_member<#cir.cxx_assign<!rec_Foo, move>> + Foo &operator=(Foo &&other) noexcept { + if (this != &other) { + a = other.a; + other.a = 0; + } + return *this; + } + + // CIR: @_ZN3FooD1Ev(!cir.ptr<!rec_Foo>) special_member<#cir.cxx_dtor<!rec_Foo>> + ~Foo(); +}; + +void trivial() { + Flub f1{}; + Flub f2 = f1; + Flub f3 = static_cast<Flub&&>(f1); + f2 = f1; + f1 = static_cast<Flub&&>(f3); +} + +void non_trivial() { + Foo f1{}; + Foo f2 = f1; + Foo f3 = static_cast<Foo&&>(f1); + f2 = f1; + f1 = static_cast<Foo&&>(f3); +} diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir index 4e79149315a9d..10df27b7e168f 100644 --- a/clang/test/CIR/IR/func.cir +++ b/clang/test/CIR/IR/func.cir @@ -152,3 +152,37 @@ cir.func @global_dtor_with_priority() global_dtor(201) { // CHECK: } } + +!rec_Foo = !cir.record<struct "Foo" {!s32i}> + +cir.func @Foo_default() special_member<#cir.cxx_ctor<!rec_Foo, default>> { + cir.return +} + +// CHECK: cir.func @Foo_default() special_member<#cir.cxx_ctor<!rec_Foo, default>> { +// CHECK: cir.return +// CHECK: } + +cir.func @Foo_trivial_copy() special_member<#cir.cxx_ctor<!rec_Foo, copy, trivial true>> { + cir.return +} + +// CHECK: cir.func @Foo_trivial_copy() special_member<#cir.cxx_ctor<!rec_Foo, copy, trivial true>> { +// CHECK: cir.return +// CHECK: } + +cir.func @Foo_destructor() special_member<#cir.cxx_dtor<!rec_Foo>> { + cir.return +} + +// CHECK: cir.func @Foo_destructor() special_member<#cir.cxx_dtor<!rec_Foo>> { +// CHECK: cir.return +// CHECK: } + +cir.func @Foo_move_assign() special_member<#cir.cxx_assign<!rec_Foo, move>> { + cir.return +} + +// CHECK: cir.func @Foo_move_assign() special_member<#cir.cxx_assign<!rec_Foo, move>> { +// CHECK: cir.return +// CHECK: } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
