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

Reply via email to