Author: Rithik Sharma Date: 2026-07-10T10:50:48-07:00 New Revision: d1dbd484ada2a323a83ec9db64bcc247badc3025
URL: https://github.com/llvm/llvm-project/commit/d1dbd484ada2a323a83ec9db64bcc247badc3025 DIFF: https://github.com/llvm/llvm-project/commit/d1dbd484ada2a323a83ec9db64bcc247badc3025.diff LOG: [CIR] Generalize the special member attribute into func_info and add a callee resolver (#207261) The `cxx_special_member` slot on `cir.func` becomes a general slot named `func_info`, so facts about a function that are not special member facts can join later without redesign. Nothing about the special member forms changes. The attribute stays the same union of the constructor, destructor, and assignment forms. Each form keeps its shape and its embedded record type, and the CXX ABI lowering still replaces that type with the converted one. The FuncOp helpers keep their names, so passes that read the special member facts do not change. The printed keyword on `cir.func` changes from `special_member` to `func_info`, so the old spelling no longer parses. The existing tests update their check lines with no other change. Calls gain a resolver named `resolveCallee` that returns the function a direct call targets, so a pass standing at a call can read the facts recorded on its callee. It returns null when the call is indirect, when the callee symbol does not resolve, and when the target is not a `cir.func`. A second form takes a symbol table collection so a pass can reuse its cached lookup. Both `cir.call` and `cir.try_call` get the resolver through one shared implementation, and a unit test drives all of these cases. Added: clang/unittests/CIR/CallOpTest.cpp Modified: clang/include/clang/CIR/Dialect/IR/CIRAttrs.td clang/include/clang/CIR/Dialect/IR/CIROps.td clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/test/CIR/CodeGen/array-init-loop-exprs.cpp clang/test/CIR/CodeGen/ctor-try-body.cpp clang/test/CIR/CodeGen/cxx-special-member-attr.cpp clang/test/CIR/CodeGen/inherited-ctors.cpp clang/test/CIR/IR/func.cir clang/test/CIR/IR/invalid-func.cir clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir clang/unittests/CIR/CMakeLists.txt Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index ec99899355b17..88e8f91f7e75c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1292,11 +1292,16 @@ def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", "cxx_assign"> { }]; } -def CIR_CXXSpecialMemberAttr : AnyAttrOf<[ +//===----------------------------------------------------------------------===// +// FuncInfoAttr +//===----------------------------------------------------------------------===// + +// The attributes accepted in the optional func_info slot on cir.func. +def CIR_FuncInfoAttr : AnyAttrOf<[ CIR_CXXCtorAttr, CIR_CXXDtorAttr, CIR_CXXAssignAttr -]>; +], "function information attribute">; //===----------------------------------------------------------------------===// // BitfieldInfoAttr diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index e83fb81291007..762ef56248e4c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4024,7 +4024,7 @@ def CIR_FuncOp : CIR_Op<"func", [ OptionalAttr<FlatSymbolRefAttr>:$personality, CIR_OptionalPriorityAttr:$global_ctor_priority, CIR_OptionalPriorityAttr:$global_dtor_priority, - OptionalAttr<CIR_CXXSpecialMemberAttr>:$cxx_special_member, + OptionalAttr<CIR_FuncInfoAttr>:$func_info, OptionalAttr<CIR_AnnotationArrayAttr>:$annotations ); @@ -4287,6 +4287,10 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []> bool isIndirect() { return !getCallee(); } mlir::Value getIndirectCall(); + /// Returns the `cir.func` being called or null if the call is indirect or + /// cannot be resolved. + cir::FuncOp resolveCalleeInTable(mlir::SymbolTableCollection &symbolTable); + void setArg(unsigned index, mlir::Value value) { if (!isIndirect()) { setOperand(index, value); @@ -4298,6 +4302,16 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []> } }]; + let extraClassDefinition = [{ + cir::FuncOp $cppClass::resolveCalleeInTable( + mlir::SymbolTableCollection &symbolTable) { + mlir::FlatSymbolRefAttr callee = getCalleeAttr(); + if (!callee) + return {}; + return symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(*this, callee); + } + }]; + let hasCustomAssemblyFormat = 1; let skipDefaultBuilders = 1; diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 6b7d44b09118b..66a9dc9ea605c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3512,7 +3512,7 @@ void CIRGenModule::setCXXSpecialMemberAttr( auto cxxDtor = cir::CXXDtorAttr::get( convertType(getASTContext().getCanonicalTagType(dtor->getParent())), dtor->isTrivial()); - funcOp.setCxxSpecialMemberAttr(cxxDtor); + funcOp.setFuncInfoAttr(cxxDtor); return; } @@ -3521,7 +3521,7 @@ void CIRGenModule::setCXXSpecialMemberAttr( auto cxxCtor = cir::CXXCtorAttr::get( convertType(getASTContext().getCanonicalTagType(ctor->getParent())), kind, ctor->isTrivial()); - funcOp.setCxxSpecialMemberAttr(cxxCtor); + funcOp.setFuncInfoAttr(cxxCtor); return; } @@ -3532,7 +3532,7 @@ void CIRGenModule::setCXXSpecialMemberAttr( auto cxxAssign = cir::CXXAssignAttr::get( convertType(getASTContext().getCanonicalTagType(method->getParent())), assignKind, method->isTrivial()); - funcOp.setCxxSpecialMemberAttr(cxxAssign); + funcOp.setFuncInfoAttr(cxxAssign); return; } } diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 67cc5e09f26d0..c519229a02f26 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2386,7 +2386,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { mlir::StringAttr comdatNameAttr = getComdatAttrName(state.name); mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name); mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name); - mlir::StringAttr specialMemberAttr = getCxxSpecialMemberAttrName(state.name); + mlir::StringAttr funcInfoNameAttr = getFuncInfoAttrName(state.name); if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref()))) state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr()); @@ -2541,19 +2541,21 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { return success(); }; - // Parse CXXSpecialMember attribute - if (parser.parseOptionalKeyword("special_member").succeeded()) { + // Parse the func_info attribute + if (parser.parseOptionalKeyword("func_info").succeeded()) { if (parser.parseLess().failed()) return failure(); + llvm::SMLoc attrLoc = parser.getCurrentLocation(); mlir::Attribute attr; if (parser.parseAttribute(attr).failed()) return failure(); if (!mlir::isa<cir::CXXCtorAttr, cir::CXXDtorAttr, cir::CXXAssignAttr>( attr)) - return parser.emitError(parser.getCurrentLocation(), - "expected a C++ special member attribute"); - state.addAttribute(specialMemberAttr, attr); + return parser.emitError(attrLoc, + "expected a function info attribute, got ") + << attr; + state.addAttribute(funcInfoNameAttr, attr); if (parser.parseGreater().failed()) return failure(); @@ -2640,26 +2642,29 @@ bool cir::FuncOp::isDeclaration() { } bool cir::FuncOp::isCXXSpecialMemberFunction() { - return getCxxSpecialMemberAttr() != nullptr; + // The func_info union can grow forms that are not special members, so the + // check names the concrete forms rather than testing for presence. + mlir::Attribute attr = getFuncInfoAttr(); + return attr && mlir::isa<CXXCtorAttr, CXXDtorAttr, CXXAssignAttr>(attr); } bool cir::FuncOp::isCxxConstructor() { - auto attr = getCxxSpecialMemberAttr(); + auto attr = getFuncInfoAttr(); return attr && dyn_cast<CXXCtorAttr>(attr); } bool cir::FuncOp::isCxxDestructor() { - auto attr = getCxxSpecialMemberAttr(); + auto attr = getFuncInfoAttr(); return attr && dyn_cast<CXXDtorAttr>(attr); } bool cir::FuncOp::isCxxSpecialAssignment() { - auto attr = getCxxSpecialMemberAttr(); + auto attr = getFuncInfoAttr(); return attr && dyn_cast<CXXAssignAttr>(attr); } std::optional<CtorKind> cir::FuncOp::getCxxConstructorKind() { - mlir::Attribute attr = getCxxSpecialMemberAttr(); + mlir::Attribute attr = getFuncInfoAttr(); if (attr) { if (auto ctor = dyn_cast<CXXCtorAttr>(attr)) return ctor.getCtorKind(); @@ -2668,7 +2673,7 @@ std::optional<CtorKind> cir::FuncOp::getCxxConstructorKind() { } std::optional<AssignKind> cir::FuncOp::getCxxSpecialAssignKind() { - mlir::Attribute attr = getCxxSpecialMemberAttr(); + mlir::Attribute attr = getFuncInfoAttr(); if (attr) { if (auto assign = dyn_cast<CXXAssignAttr>(attr)) return assign.getAssignKind(); @@ -2677,7 +2682,7 @@ std::optional<AssignKind> cir::FuncOp::getCxxSpecialAssignKind() { } bool cir::FuncOp::isCxxTrivialMemberFunction() { - mlir::Attribute attr = getCxxSpecialMemberAttr(); + mlir::Attribute attr = getFuncInfoAttr(); if (attr) { if (auto ctor = dyn_cast<CXXCtorAttr>(attr)) return ctor.getIsTrivial(); @@ -2750,9 +2755,9 @@ void cir::FuncOp::print(OpAsmPrinter &p) { p << ")"; } - if (auto specialMemberAttr = getCxxSpecialMember()) { - p << " special_member<"; - p.printAttribute(*specialMemberAttr); + if (mlir::Attribute funcInfo = getFuncInfoAttr()) { + p << " func_info<"; + p.printAttribute(funcInfo); p << '>'; } diff --git a/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp b/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp index b8fc4b17ea38d..25ffaca4a6807 100644 --- a/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp +++ b/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp @@ -17,7 +17,7 @@ struct HasNonTrivialArray { NonTrivial arr[3]; }; -// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN18HasNonTrivialArrayC2ERKS_({{.*}}) special_member<#cir.cxx_ctor<!rec_HasNonTrivialArray, copy>> +// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN18HasNonTrivialArrayC2ERKS_({{.*}}) func_info<#cir.cxx_ctor<!rec_HasNonTrivialArray, copy>> // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>> // CIR: %[[RHS_ALLOCA:.*]] = cir.alloca "" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>> // CIR: %[[ITR_ALLOCA:.*]] = cir.alloca "arrayinit.temp" {{.*}} : !cir.ptr<!cir.ptr<!rec_NonTrivial>> @@ -137,7 +137,7 @@ struct HasMultiDimArray { NonTrivial arr[2][3][4]; }; -// CIR-LABEL: cir.func {{.*}}@_ZN16HasMultiDimArrayC2ERKS_({{.*}}) special_member<#cir.cxx_ctor<!rec_HasMultiDimArray, copy>> +// CIR-LABEL: cir.func {{.*}}@_ZN16HasMultiDimArrayC2ERKS_({{.*}}) func_info<#cir.cxx_ctor<!rec_HasMultiDimArray, copy>> // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_HasMultiDimArray>> // CIR: %[[RHS_ALLOCA:.*]] = cir.alloca "" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_HasMultiDimArray>> // CIR: %[[ITR1_ALLOCA:.*]] = cir.alloca "arrayinit.temp" {{.*}} : !cir.ptr<!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>> diff --git a/clang/test/CIR/CodeGen/ctor-try-body.cpp b/clang/test/CIR/CodeGen/ctor-try-body.cpp index 503be3f273c70..21967ceebfc10 100644 --- a/clang/test/CIR/CodeGen/ctor-try-body.cpp +++ b/clang/test/CIR/CodeGen/ctor-try-body.cpp @@ -30,7 +30,7 @@ struct HasThings : Base { side_effect2(); } -// CIR: cir.func {{.*}}@_ZN9HasThingsC2ERK4Ctor(%[[THIS_ARG:.*]]: !cir.ptr<!rec_HasThings> {{.*}}, %[[C_ARG:.*]]: !cir.ptr<!rec_Ctor> {{.*}}) {{.*}}special_member<#cir.cxx_ctor<!rec_HasThings, custom>>{{.*}} { +// CIR: cir.func {{.*}}@_ZN9HasThingsC2ERK4Ctor(%[[THIS_ARG:.*]]: !cir.ptr<!rec_HasThings> {{.*}}, %[[C_ARG:.*]]: !cir.ptr<!rec_Ctor> {{.*}}) {{.*}}func_info<#cir.cxx_ctor<!rec_HasThings, custom>>{{.*}} { // CIR-NEXT: %[[THIS_ALLOC:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_HasThings>> // CIR-NEXT: %[[C_ALLOC:.*]] = cir.alloca "c" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_Ctor>> // CIR-NEXT: cir.store %[[THIS_ARG]], %[[THIS_ALLOC]] : !cir.ptr<!rec_HasThings>, !cir.ptr<!cir.ptr<!rec_HasThings>> diff --git a/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp index bfca59db44fdf..6a43ceeb97b48 100644 --- a/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp +++ b/clang/test/CIR/CodeGen/cxx-special-member-attr.cpp @@ -31,8 +31,8 @@ struct 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>> +// CIR: @_ZN4FlubaSERKS_(%arg0: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Flub> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Flub>{{.*}}) func_info<#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>{{.*}}) func_info<#cir.cxx_assign<!rec_Flub, move, trivial true>> void trivial_func() { Flub f1{}; @@ -50,18 +50,18 @@ void trivial_func() { void non_trivial_func() { Foo f1{}; - // CIR: @_ZN3FooC2Ev(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Foo, default>> + // CIR: @_ZN3FooC2Ev(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) func_info<#cir.cxx_ctor<!rec_Foo, default>> Foo f2 = f1; - // CIR: @_ZN3FooC2ERKS_(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Foo, copy>> + // CIR: @_ZN3FooC2ERKS_(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) func_info<#cir.cxx_ctor<!rec_Foo, copy>> Foo f3 = static_cast<Foo&&>(f1); - // CIR: @_ZN3FooC2EOS_(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) special_member<#cir.cxx_ctor<!rec_Foo, move>> + // CIR: @_ZN3FooC2EOS_(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) func_info<#cir.cxx_ctor<!rec_Foo, move>> f2 = f1; - // 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>> + // CIR: @_ZN3FooaSERKS_(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Foo>{{.*}}) func_info<#cir.cxx_assign<!rec_Foo, copy>> f1 = static_cast<Foo&&>(f3); - // 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>> - // CIR: @_ZN3FooD1Ev(!cir.ptr<!rec_Foo> {{.*}}) special_member<#cir.cxx_dtor<!rec_Foo>> + // CIR: @_ZN3FooaSEOS_(%arg0: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}}), %arg1: !cir.ptr<!rec_Foo> {{[{][^}]*[}]}} loc({{.*}})) -> (!cir.ptr<!rec_Foo>{{.*}}) func_info<#cir.cxx_assign<!rec_Foo, move>> + // CIR: @_ZN3FooD1Ev(!cir.ptr<!rec_Foo> {{.*}}) func_info<#cir.cxx_dtor<!rec_Foo>> } diff --git a/clang/test/CIR/CodeGen/inherited-ctors.cpp b/clang/test/CIR/CodeGen/inherited-ctors.cpp index 1a5a3eb6f8479..ce6df242df742 100644 --- a/clang/test/CIR/CodeGen/inherited-ctors.cpp +++ b/clang/test/CIR/CodeGen/inherited-ctors.cpp @@ -38,11 +38,11 @@ void fallsthrough() { // LLVM and OGCG check labels are identical other than the 1 diff erence called out (and ordering). -// CIR-LABEL: cir.func private @_ZN4BaseC2Ei(!cir.ptr<!rec_Base>{{.*}}, !s32i{{.*}}) special_member<#cir.cxx_ctor<!rec_Base, custom>> +// CIR-LABEL: cir.func private @_ZN4BaseC2Ei(!cir.ptr<!rec_Base>{{.*}}, !s32i{{.*}}) func_info<#cir.cxx_ctor<!rec_Base, custom>> // LLVM-LABEL: declare void @_ZN4BaseC2Ei(ptr {{.*}}, i32 {{.*}}) // // -// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN7DerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_Derived>{{.*}}, %{{.*}}: !s32i{{.*}}) special_member<#cir.cxx_ctor<!rec_Derived, custom>> +// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN7DerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_Derived>{{.*}}, %{{.*}}: !s32i{{.*}}) func_info<#cir.cxx_ctor<!rec_Derived, custom>> // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_Derived>> // CIR: %[[INT_ALLOCA:.*]] = cir.alloca "" {{.*}} init : !cir.ptr<!s32i> // CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> @@ -63,7 +63,7 @@ void fallsthrough() { // LLVM-LABEL: define dso_local void @_Z20emitDelegateCallArgsv() // LLVM: call void @_ZN7DerivedCI14BaseEi(ptr {{.*}}, i32 {{.*}}1) // -// CIR-LABEL: cir.func private @_ZN4BaseC2Efz(!cir.ptr<!rec_Base>{{.*}}, !cir.float{{.*}}, ...) special_member<#cir.cxx_ctor<!rec_Base, custom>> +// CIR-LABEL: cir.func private @_ZN4BaseC2Efz(!cir.ptr<!rec_Base>{{.*}}, !cir.float{{.*}}, ...) func_info<#cir.cxx_ctor<!rec_Base, custom>> // LLVM-LABEL: declare void @_ZN4BaseC2Efz(ptr {{.*}}, float {{.*}}, ...) // // CIR-LABEL: cir.func no_inline dso_local @_Z26cannotEmitDelegateCallArgsv() @@ -80,7 +80,7 @@ void fallsthrough() { // LLVM: %[[TMP_LOAD:.*]] = load ptr, ptr %[[TMP_ALLOCA]] // LLVM: call void (ptr, float, ...) @_ZN4BaseC2Efz(ptr {{.*}}%[[TMP_LOAD]], float {{.*}}1.100000e+00, i32 {{.*}}2, double {{.*}}3.000000e+00) // -// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN11VirtDerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_VirtDerived> {{.*}}, %{{.*}}: !cir.ptr<!cir.ptr<!void>>{{.*}}) special_member<#cir.cxx_ctor<!rec_VirtDerived, custom>> +// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN11VirtDerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_VirtDerived> {{.*}}, %{{.*}}: !cir.ptr<!cir.ptr<!void>>{{.*}}) func_info<#cir.cxx_ctor<!rec_VirtDerived, custom>> // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" align(8) init : !cir.ptr<!cir.ptr<!rec_VirtDerived>> // CIR: %[[VTT_ALLOCA:.]] = cir.alloca "vtt" align(8) init : !cir.ptr<!cir.ptr<!cir.ptr<!void>>> // CIR: %[[THIS:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtDerived>>, !cir.ptr<!rec_VirtDerived> @@ -100,7 +100,7 @@ void fallsthrough() { // LLVM: store ptr %[[VTT_ADDR_LOAD]], ptr %[[THIS]] // // -// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN21VirtualDelegatingCtorC1Ei(%{{.*}}: !cir.ptr<!rec_VirtualDelegatingCtor> {{.*}}, %{{.*}}: !s32i {{.*}}) special_member<#cir.cxx_ctor<!rec_VirtualDelegatingCtor, custom>> +// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN21VirtualDelegatingCtorC1Ei(%{{.*}}: !cir.ptr<!rec_VirtualDelegatingCtor> {{.*}}, %{{.*}}: !s32i {{.*}}) func_info<#cir.cxx_ctor<!rec_VirtualDelegatingCtor, custom>> // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" align(8) init : !cir.ptr<!cir.ptr<!rec_VirtualDelegatingCtor>> // CIR: %[[X_ALLOCA:.*]] = cir.alloca "x" align(4) init : !cir.ptr<!s32i> // CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtualDelegatingCtor>>, !cir.ptr<!rec_VirtualDelegatingCtor> diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir index 6d327d60b3266..4c6bd33b386ab 100644 --- a/clang/test/CIR/IR/func.cir +++ b/clang/test/CIR/IR/func.cir @@ -166,35 +166,35 @@ cir.func @global_dtor_with_priority() global_dtor(201) { !rec_Foo = !cir.struct<"Foo" {!s32i}> -cir.func @Foo_default() special_member<#cir.cxx_ctor<!rec_Foo, default>> { +cir.func @Foo_default() func_info<#cir.cxx_ctor<!rec_Foo, default>> { cir.return } -// CHECK: cir.func @Foo_default() special_member<#cir.cxx_ctor<!rec_Foo, default>> { +// CHECK: cir.func @Foo_default() func_info<#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.func @Foo_trivial_copy() func_info<#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.func @Foo_trivial_copy() func_info<#cir.cxx_ctor<!rec_Foo, copy, trivial true>> { // CHECK: cir.return // CHECK: } -cir.func @Foo_destructor() special_member<#cir.cxx_dtor<!rec_Foo>> { +cir.func @Foo_destructor() func_info<#cir.cxx_dtor<!rec_Foo>> { cir.return } -// CHECK: cir.func @Foo_destructor() special_member<#cir.cxx_dtor<!rec_Foo>> { +// CHECK: cir.func @Foo_destructor() func_info<#cir.cxx_dtor<!rec_Foo>> { // CHECK: cir.return // CHECK: } -cir.func @Foo_move_assign() special_member<#cir.cxx_assign<!rec_Foo, move>> { +cir.func @Foo_move_assign() func_info<#cir.cxx_assign<!rec_Foo, move>> { cir.return } -// CHECK: cir.func @Foo_move_assign() special_member<#cir.cxx_assign<!rec_Foo, move>> { +// CHECK: cir.func @Foo_move_assign() func_info<#cir.cxx_assign<!rec_Foo, move>> { // CHECK: cir.return // CHECK: } diff --git a/clang/test/CIR/IR/invalid-func.cir b/clang/test/CIR/IR/invalid-func.cir index 5fc1c2a53db09..66f73aadc8cc9 100644 --- a/clang/test/CIR/IR/invalid-func.cir +++ b/clang/test/CIR/IR/invalid-func.cir @@ -13,7 +13,16 @@ module { // ----- module { - cir.func @l0() special_member<> { // expected-error {{expected attribute value}} + cir.func @l0() func_info<> { // expected-error {{expected attribute value}} + cir.return + } +} + +// ----- + +module { + // expected-error@below {{expected a function info attribute, got 32 : i32}} + cir.func @l0() func_info<32 : i32> { cir.return } } diff --git a/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir b/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir index 75bc8471dfe9d..2faf2acf421d4 100644 --- a/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir +++ b/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir @@ -54,35 +54,35 @@ cir.func @TypeAttrs(%arg0 : !rec_Struct, %arg1 : !dmp, %arg2 : !dmp_ptr) -> (!re } // cir::CXXCtorAttr -cir.func @ctor() special_member<#cir.cxx_ctor<!rec_Struct, default>> { -// CHECK: cir.func @ctor() special_member<#cir.cxx_ctor<!rec_Struct, default>> { +cir.func @ctor() func_info<#cir.cxx_ctor<!rec_Struct, default>> { +// CHECK: cir.func @ctor() func_info<#cir.cxx_ctor<!rec_Struct, default>> { cir.trap } -cir.func @ctor2() special_member<#cir.cxx_ctor<!dmp, default>> { -// CHECK: cir.func @ctor2() special_member<#cir.cxx_ctor<!s64i, default>> { +cir.func @ctor2() func_info<#cir.cxx_ctor<!dmp, default>> { +// CHECK: cir.func @ctor2() func_info<#cir.cxx_ctor<!s64i, default>> { cir.trap } // cir::CXXDtorAttr -cir.func @dtor() special_member<#cir.cxx_dtor<!rec_Struct>> { -// CHECK: cir.func @dtor() special_member<#cir.cxx_dtor<!rec_Struct>> { +cir.func @dtor() func_info<#cir.cxx_dtor<!rec_Struct>> { +// CHECK: cir.func @dtor() func_info<#cir.cxx_dtor<!rec_Struct>> { cir.trap } -cir.func @dtor2() special_member<#cir.cxx_dtor<!dmp>> { -// CHECK: cir.func @dtor2() special_member<#cir.cxx_dtor<!s64i>> { +cir.func @dtor2() func_info<#cir.cxx_dtor<!dmp>> { +// CHECK: cir.func @dtor2() func_info<#cir.cxx_dtor<!s64i>> { cir.trap } // cir::CXXAssignAttr -cir.func @assign() special_member<#cir.cxx_assign<!rec_Struct, move>> { -// CHECK: cir.func @assign() special_member<#cir.cxx_assign<!rec_Struct, move>> { +cir.func @assign() func_info<#cir.cxx_assign<!rec_Struct, move>> { +// CHECK: cir.func @assign() func_info<#cir.cxx_assign<!rec_Struct, move>> { cir.trap } -cir.func @assign2() special_member<#cir.cxx_assign<!dmp, move>> { -// CHECK: cir.func @assign2() special_member<#cir.cxx_assign<!s64i, move>> { +cir.func @assign2() func_info<#cir.cxx_assign<!dmp, move>> { +// CHECK: cir.func @assign2() func_info<#cir.cxx_assign<!s64i, move>> { cir.trap } } diff --git a/clang/unittests/CIR/CMakeLists.txt b/clang/unittests/CIR/CMakeLists.txt index 81ef87878d33e..a431356044cfc 100644 --- a/clang/unittests/CIR/CMakeLists.txt +++ b/clang/unittests/CIR/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories(SYSTEM ${MLIR_INCLUDE_DIR}) include_directories(${MLIR_TABLEGEN_OUTPUT_DIR}) add_distinct_clang_unittest(CIRUnitTests + CallOpTest.cpp ControlFlowTest.cpp PointerLikeTest.cpp RecordTypeMetadataTest.cpp diff --git a/clang/unittests/CIR/CallOpTest.cpp b/clang/unittests/CIR/CallOpTest.cpp new file mode 100644 index 0000000000000..5b2e4780b1496 --- /dev/null +++ b/clang/unittests/CIR/CallOpTest.cpp @@ -0,0 +1,97 @@ +//===- CallOpTest.cpp - Unit tests for cir.call helpers -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/OwningOpRef.h" +#include "mlir/IR/SymbolTable.h" +#include "mlir/Parser/Parser.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + +#include <gtest/gtest.h> + +using namespace mlir; + +namespace { + +class CIRCallOpTest : public ::testing::Test { +protected: + CIRCallOpTest() { context.loadDialect<cir::CIRDialect>(); } + + OwningOpRef<ModuleOp> parse(StringRef ir) { + auto module = parseSourceString<ModuleOp>(ir, &context); + EXPECT_TRUE(module) << "failed to parse IR"; + return module; + } + + MLIRContext context; +}; + +// A callee that carries func_info, a callee that carries none, an indirect +// call with no callee symbol at all, and a cir.try_call to the marked callee. +constexpr const char *moduleText = R"CIR( +!s32i = !cir.int<s, 32> +!rec_S = !cir.struct<"S" {!s32i}> + +module { + cir.func @marked() func_info<#cir.cxx_ctor<!rec_S, default>> { + cir.return + } + cir.func @plain() { + cir.return + } + cir.func @caller(%arg0: !cir.ptr<!cir.func<()>>) { + cir.call @marked() : () -> () + cir.call @plain() : () -> () + cir.call %arg0() : (!cir.ptr<!cir.func<()>>) -> () + cir.return + } + cir.func @try_caller() { + cir.try_call @marked() ^bb1, ^bb2 : () -> () + ^bb1: + cir.return + ^bb2: + cir.return + } +} +)CIR"; + +TEST_F(CIRCallOpTest, ResolveCallee) { + OwningOpRef<ModuleOp> module = parse(moduleText); + ASSERT_TRUE(module); + + SmallVector<cir::CallOp> calls; + module->walk([&](cir::CallOp op) { calls.push_back(op); }); + ASSERT_EQ(calls.size(), 3u); + + SymbolTableCollection symbolTable; + + // A direct call resolves to its callee, and the caller can then read the + // facts recorded on it, such as the func_info attribute. + cir::FuncOp marked = calls[0].resolveCalleeInTable(symbolTable); + ASSERT_TRUE(marked); + auto ctor = dyn_cast_or_null<cir::CXXCtorAttr>(marked.getFuncInfoAttr()); + ASSERT_TRUE(ctor); + EXPECT_EQ(ctor.getCtorKind(), cir::CtorKind::Default); + + // A callee without func_info still resolves, and the attribute reads null. + cir::FuncOp plain = calls[1].resolveCalleeInTable(symbolTable); + ASSERT_TRUE(plain); + EXPECT_FALSE(plain.getFuncInfoAttr()); + + // An indirect call has no callee symbol to resolve. + EXPECT_FALSE(calls[2].resolveCalleeInTable(symbolTable)); + + // cir.try_call resolves through the same implementation. + SmallVector<cir::TryCallOp> tryCalls; + module->walk([&](cir::TryCallOp op) { tryCalls.push_back(op); }); + ASSERT_EQ(tryCalls.size(), 1u); + EXPECT_EQ(tryCalls[0].resolveCalleeInTable(symbolTable), marked); +} + +} // namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
