diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 869eb54ec9e8..3d10a2ab5145 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1271,6 +1271,60 @@ def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> { }]; } +def CIR_CtorKind : CIR_I32EnumAttr<"CtorKind", "CXX Constructor Kind", [ + I32EnumAttrCase<"Custom", 0, "custom">, + I32EnumAttrCase<"Default", 1, "default">, + I32EnumAttrCase<"Copy", 2, "copy">, +]> { + let genSpecializedAttr = 0; +} + +def CIR_CXXCtorAttr : CIR_Attr<"CXXCtor", "cxx_ctor"> { + let summary = "Marks a function as a CXX constructor"; + let description = [{ + Functions with this attribute are CXX constructors. + The `custom` kind is used if the constructor is a custom constructor. + The `default` kind is used if the constructor is a default constructor. + The `copy` kind is used if the constructor is a copy constructor. + }]; + let parameters = (ins "mlir::Type":$type, + EnumParameter:$ctorKind); + + let assemblyFormat = [{ + `<` $type `,` $ctorKind `>` + }]; + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type, + CArg<"CtorKind", "cir::CtorKind::Custom">:$ctorKind), [{ + return $_get(type.getContext(), type, ctorKind); + }]> + ]; +} + +def CIR_CXXDtorAttr : CIR_Attr<"CXXDtor", "cxx_dtor"> { + let summary = "Marks a function as a CXX destructor"; + let description = [{ + Functions with this attribute are CXX destructors + }]; + let parameters = (ins "mlir::Type":$type); + + let assemblyFormat = [{ + `<` $type `>` + }]; + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type), [{ + return $_get(type.getContext(), type); + }]> + ]; +} + +def CIR_CXXSpecialMemberAttr : AnyAttrOf<[ + CIR_CXXCtorAttr, + CIR_CXXDtorAttr +]>; + def CIR_BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> { let summary = "Represents a bit field info"; let description = [{ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index b8abd89f35d2..dc3cfad8fbe3 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3724,6 +3724,7 @@ def FuncOp : CIR_Op<"func", [ CIR_OptionalPriorityAttr:$global_ctor_priority, CIR_OptionalPriorityAttr:$global_dtor_priority, OptionalAttr:$annotations, + OptionalAttr:$cxx_special_member, OptionalAttr:$ast ); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index a5f9fba96967..da11937e2417 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -764,15 +764,29 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, // Generate the body of the function. // TODO: PGO.assignRegionCounters assert(!cir::MissingFeatures::shouldInstrumentFunction()); - if (isa(fd)) + if (auto dtor = dyn_cast(fd)) { + auto cxxDtor = cir::CXXDtorAttr::get( + convertType(getContext().getRecordType(dtor->getParent()))); + fn.setCxxSpecialMemberAttr(cxxDtor); + emitDestructorBody(args); - else if (isa(fd)) + } else if (auto ctor = dyn_cast(fd)) { + cir::CtorKind ctorKind = cir::CtorKind::Custom; + if (ctor->isDefaultConstructor()) + ctorKind = cir::CtorKind::Default; + if (ctor->isCopyConstructor()) + ctorKind = cir::CtorKind::Copy; + + auto cxxCtor = cir::CXXCtorAttr::get( + convertType(getContext().getRecordType(ctor->getParent())), ctorKind); + fn.setCxxSpecialMemberAttr(cxxCtor); + emitConstructorBody(args); - else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && - fd->hasAttr()) + } else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && + fd->hasAttr()) { CGM.getCUDARuntime().emitDeviceStub(*this, fn, args); - else if (isa(fd) && - cast(fd)->isLambdaStaticInvoker()) { + } else if (isa(fd) && + cast(fd)->isLambdaStaticInvoker()) { // The lambda static invoker function is special, because it forwards or // clones the body of the function call operator (but is actually // static). @@ -788,8 +802,9 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, fn.erase(); return nullptr; } - } else + } else { llvm_unreachable("no definition for emitted function"); + } assert(builder.getInsertionBlock() && "Should be valid"); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index d64adef8c6ab..19bfc4fdb834 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2735,6 +2735,27 @@ cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, f.setExtraAttrsAttr( cir::ExtraFuncAttributesAttr::get(builder.getDictionaryAttr({}))); + if (fd) { + if (auto dtor = dyn_cast(fd)) { + auto cxxDtor = cir::CXXDtorAttr::get( + convertType(getASTContext().getRecordType(dtor->getParent()))); + f.setCxxSpecialMemberAttr(cxxDtor); + } + + if (auto ctor = dyn_cast(fd)) { + cir::CtorKind ctorKind = cir::CtorKind::Custom; + if (ctor->isDefaultConstructor()) + ctorKind = cir::CtorKind::Default; + if (ctor->isCopyConstructor()) + ctorKind = cir::CtorKind::Copy; + + auto cxxCtor = cir::CXXCtorAttr::get( + convertType(getASTContext().getRecordType(ctor->getParent())), + ctorKind); + f.setCxxSpecialMemberAttr(cxxCtor); + } + } + if (!curCGF) theModule.push_back(f); } diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 18ef47095e6e..3606baa7c75b 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2529,6 +2529,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { auto visibilityNameAttr = getGlobalVisibilityAttrName(state.name); auto dsoLocalNameAttr = getDsoLocalAttrName(state.name); auto annotationsNameAttr = getAnnotationsAttrName(state.name); + auto cxxSpecialMemberAttr = getCxxSpecialMemberAttrName(state.name); if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref()))) state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr()); if (::mlir::succeeded( @@ -2609,6 +2610,20 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { state.addAttribute(annotationsNameAttr, annotations); } + // Parse CXXSpecialMember attribute + if (parser.parseOptionalKeyword("special_member").succeeded()) { + cir::CXXCtorAttr ctorAttr; + cir::CXXDtorAttr dtorAttr; + if (parser.parseLess().failed()) + return failure(); + if (parser.parseOptionalAttribute(ctorAttr).has_value()) + state.addAttribute(cxxSpecialMemberAttr, ctorAttr); + if (parser.parseOptionalAttribute(dtorAttr).has_value()) + state.addAttribute(cxxSpecialMemberAttr, dtorAttr); + if (parser.parseGreater().failed()) + return failure(); + } + // If additional attributes are present, parse them. if (parser.parseOptionalAttrDictWithKeyword(state.attributes)) return failure(); @@ -2789,6 +2804,13 @@ void cir::FuncOp::print(OpAsmPrinter &p) { p.printAttribute(annotations); } + if (auto specialMemberAttr = getCxxSpecialMember()) { + assert((mlir::isa(*specialMemberAttr))); + p << " special_member<"; + p.printAttribute(*specialMemberAttr); + p << '>'; + } + function_interface_impl::printFunctionAttributes( p, *this, // These are all omitted since they are custom printed already. @@ -2799,7 +2821,7 @@ void cir::FuncOp::print(OpAsmPrinter &p) { getLinkageAttrName(), getCallingConvAttrName(), getNoProtoAttrName(), getSymVisibilityAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(), getComdatAttrName(), getGlobalVisibilityAttrName(), - getAnnotationsAttrName()}); + getAnnotationsAttrName(), getCxxSpecialMemberAttrName()}); if (auto aliaseeName = getAliasee()) { p << " alias("; diff --git a/clang/lib/CIR/Dialect/Transforms/LifetimeCheck.cpp b/clang/lib/CIR/Dialect/Transforms/LifetimeCheck.cpp index af909005b4a0..8d5e2fb1efbc 100644 --- a/clang/lib/CIR/Dialect/Transforms/LifetimeCheck.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LifetimeCheck.cpp @@ -76,7 +76,7 @@ struct LifetimeCheckPass : public LifetimeCheckBase { void checkLambdaCaptureStore(StoreOp storeOp); void trackCallToCoroutine(CallOp callOp); - void checkCtor(CallOp callOp, ASTCXXConstructorDeclInterface ctor); + void checkCtor(CallOp callOp, cir::CXXCtorAttr ctor); void checkMoveAssignment(CallOp callOp, ASTCXXMethodDeclInterface m); void checkCopyAssignment(CallOp callOp, ASTCXXMethodDeclInterface m); void checkNonConstUseOfOwner(mlir::Value ownerAddr, mlir::Location loc); @@ -1549,8 +1549,7 @@ bool LifetimeCheckPass::isCtorInitPointerFromOwner(CallOp callOp) { return false; } -void LifetimeCheckPass::checkCtor(CallOp callOp, - ASTCXXConstructorDeclInterface ctor) { +void LifetimeCheckPass::checkCtor(CallOp callOp, cir::CXXCtorAttr ctor) { // TODO: zero init // 2.4.2 if the initialization is default initialization or zero // initialization, example: @@ -1559,7 +1558,7 @@ void LifetimeCheckPass::checkCtor(CallOp callOp, // string_view p; // // both results in pset(p) == {null} - if (ctor.isDefaultConstructor()) { + if (ctor.getCtorKind() == cir::CtorKind::Default) { // First argument passed is always the alloca for the 'this' ptr. // Currently two possible actions: @@ -1583,7 +1582,7 @@ void LifetimeCheckPass::checkCtor(CallOp callOp, } // User defined copy ctor calls ... - if (ctor.isCopyConstructor()) { + if (ctor.getCtorKind() == cir::CtorKind::Copy) { llvm_unreachable("NYI"); } @@ -1788,8 +1787,13 @@ void LifetimeCheckPass::checkCall(CallOp callOp) { // From this point on only owner and pointer class methods handling, // starting from special methods. - if (auto ctor = dyn_cast(methodDecl)) - return checkCtor(callOp, ctor); + if (auto fnName = callOp.getCallee()) { + auto calleeFuncOp = getCalleeFromSymbol(theModule, *fnName); + if (calleeFuncOp && calleeFuncOp.getCxxSpecialMember()) + if (auto cxxCtor = + dyn_cast(*calleeFuncOp.getCxxSpecialMember())) + return checkCtor(callOp, cxxCtor); + } if (methodDecl.isMoveAssignmentOperator()) return checkMoveAssignment(callOp, methodDecl); if (methodDecl.isCopyAssignmentOperator()) diff --git a/clang/test/CIR/CodeGen/ctor-alias.cpp b/clang/test/CIR/CodeGen/ctor-alias.cpp index 4afbdc12b3c1..5c27762bdfe3 100644 --- a/clang/test/CIR/CodeGen/ctor-alias.cpp +++ b/clang/test/CIR/CodeGen/ctor-alias.cpp @@ -37,4 +37,4 @@ B::B() { // CHECK: %1 = cir.load %0 : !cir.ptr>, !cir.ptr // CHECK: cir.return // CHECK: } -// CHECK: cir.func private dso_local @_ZN1BC1Ev(!cir.ptr) alias(@_ZN1BC2Ev) +// CHECK: cir.func private dso_local @_ZN1BC1Ev(!cir.ptr) special_member<#cir.cxx_ctor> alias(@_ZN1BC2Ev) diff --git a/clang/test/CIR/CodeGen/static.cpp b/clang/test/CIR/CodeGen/static.cpp index 4f1920e3f135..b0d6c6264c1e 100644 --- a/clang/test/CIR/CodeGen/static.cpp +++ b/clang/test/CIR/CodeGen/static.cpp @@ -18,7 +18,7 @@ static Init __ioinit2(false); // BEFORE: module {{.*}} { // BEFORE-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr, !cir.bool) -// BEFORE-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr) +// BEFORE-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> // BEFORE-NEXT: cir.global "private" internal dso_local @_ZL8__ioinit = ctor : !rec_Init { // BEFORE-NEXT: %0 = cir.get_global @_ZL8__ioinit : !cir.ptr // BEFORE-NEXT: %1 = cir.const #true @@ -42,7 +42,7 @@ static Init __ioinit2(false); // AFTER-NEXT: cir.global "private" external @__dso_handle : i8 // AFTER-NEXT: cir.func private @__cxa_atexit(!cir.ptr)>>, !cir.ptr, !cir.ptr) // AFTER-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr, !cir.bool) -// AFTER-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr) +// AFTER-NEXT: cir.func private @_ZN4InitD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> // AFTER-NEXT: cir.global "private" internal dso_local @_ZL8__ioinit = #cir.zero : !rec_Init {alignment = 1 : i64, ast = #cir.var.decl.ast} // AFTER-NEXT: cir.func internal private @__cxx_global_var_init() // AFTER-NEXT: %0 = cir.get_global @_ZL8__ioinit : !cir.ptr diff --git a/clang/test/CIR/CodeGen/temporaries.cpp b/clang/test/CIR/CodeGen/temporaries.cpp index 1ac0b9a35014..d104df4f7a24 100644 --- a/clang/test/CIR/CodeGen/temporaries.cpp +++ b/clang/test/CIR/CodeGen/temporaries.cpp @@ -14,9 +14,9 @@ void f() { !E(); } -// CIR: cir.func private @_ZN1EC1Ev(!cir.ptr) extra(#fn_attr) +// CIR: cir.func private @_ZN1EC1Ev(!cir.ptr) special_member<#cir.cxx_ctor> extra(#fn_attr) // CIR-NEXT: cir.func private @_ZN1EntEv(!cir.ptr) -> !rec_E -// CIR-NEXT: cir.func private @_ZN1ED1Ev(!cir.ptr) extra(#fn_attr) +// CIR-NEXT: cir.func private @_ZN1ED1Ev(!cir.ptr) special_member<#cir.cxx_dtor> extra(#fn_attr) // CIR-NEXT: cir.func dso_local @_Z1fv() extra(#fn_attr1) { // CIR-NEXT: cir.scope { // CIR-NEXT: %[[ONE:[0-9]+]] = cir.alloca !rec_E, !cir.ptr, ["agg.tmp.ensured"] {alignment = 1 : i64} diff --git a/clang/test/CIR/CodeGen/tempref.cpp b/clang/test/CIR/CodeGen/tempref.cpp index 76cd0d1a38f7..c3bba80ecbda 100644 --- a/clang/test/CIR/CodeGen/tempref.cpp +++ b/clang/test/CIR/CodeGen/tempref.cpp @@ -6,7 +6,7 @@ struct A { ~A(); }; A &&a = dynamic_cast(A{}); -// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr) extra(#fn_attr) +// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> extra(#fn_attr) // CHECK-NEXT: cir.global external @a = #cir.ptr : !cir.ptr {alignment = 8 : i64, ast = #cir.var.decl.ast} // CHECK-NEXT: cir.func internal private @__cxx_global_var_init() { // CHECK-NEXT: cir.scope { diff --git a/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp b/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp index c2a1034e2669..8fb3dfdae335 100644 --- a/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp +++ b/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp @@ -37,7 +37,7 @@ struct B : A { // LLVM: call void @_ZN1AD2Ev // Complete dtor: just an alias because there are no virtual bases. -// CIR: cir.func private dso_local @_ZN1BD1Ev(!cir.ptr) alias(@_ZN1BD2Ev) +// CIR: cir.func private dso_local @_ZN1BD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> alias(@_ZN1BD2Ev) // Deleting dtor: defers to the complete dtor. // LLVM: define{{.*}} void @_ZN1BD0Ev(ptr @@ -46,11 +46,11 @@ struct B : A { // (aliases from C) // CIR: cir.func dso_local @_ZN1CD2Ev(%arg0: !cir.ptr{{.*}})) {{.*}} { -// CIR: cir.func private dso_local @_ZN1CD1Ev(!cir.ptr) alias(@_ZN1CD2Ev) +// CIR: cir.func private dso_local @_ZN1CD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> alias(@_ZN1CD2Ev) // CIR_O1-NOT: cir.func dso_local @_ZN1CD2Ev(%arg0: !cir.ptr{{.*}})) {{.*}} { -// CIR_O1: cir.func private dso_local @_ZN1CD2Ev(!cir.ptr) alias(@_ZN1BD2Ev) -// CIR_O1: cir.func private dso_local @_ZN1CD1Ev(!cir.ptr) alias(@_ZN1CD2Ev) +// CIR_O1: cir.func private dso_local @_ZN1CD2Ev(!cir.ptr) special_member<#cir.cxx_dtor> alias(@_ZN1BD2Ev) +// CIR_O1: cir.func private dso_local @_ZN1CD1Ev(!cir.ptr) special_member<#cir.cxx_dtor> alias(@_ZN1CD2Ev) // FIXME: LLVM output should be: @_ZN1CD2Ev ={{.*}} unnamed_addr alias {{.*}} @_ZN1BD2Ev // LLVM: define dso_local void @_ZN1CD2Ev(ptr diff --git a/clang/test/CIR/IR/cxx-special-member.cir b/clang/test/CIR/IR/cxx-special-member.cir new file mode 100644 index 000000000000..b28051826fcd --- /dev/null +++ b/clang/test/CIR/IR/cxx-special-member.cir @@ -0,0 +1,20 @@ +// RUN: cir-opt %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +!s32i = !cir.int +!rec_S = !cir.record +module { + cir.func private @_ZN1SC1ERKS_(!cir.ptr, !cir.ptr) special_member<#cir.cxx_ctor> + cir.func private @_ZN1SC2Ei(!cir.ptr, !cir.ptr) + cir.func private @_ZN1SC2Ev(!cir.ptr) special_member<#cir.cxx_ctor> + cir.func private @_ZN1SD2Ev(!cir.ptr) special_member<#cir.cxx_dtor> +} + +// CHECK: !s32i = !cir.int +// CHECK: !rec_S = !cir.record +// CHECK: module { +// CHECK: cir.func private @_ZN1SC1ERKS_(!cir.ptr, !cir.ptr) special_member<#cir.cxx_ctor> +// CHECK: cir.func private @_ZN1SC2Ei(!cir.ptr, !cir.ptr) +// CHECK: cir.func private @_ZN1SC2Ev(!cir.ptr) special_member<#cir.cxx_ctor> +// CHECK: cir.func private @_ZN1SD2Ev(!cir.ptr) special_member<#cir.cxx_dtor> +// CHECK: }