https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/164518

>From 27fdb6b01365b537e8c317d32e7ff4da8cf656d3 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Tue, 21 Oct 2025 16:18:19 -0700
Subject: [PATCH 1/2] [CIR] Upstream support for calling functions via member
 expressions

This adds support for calling functions via class member function pointers.
---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  8 ++---
 .../CodeGen/call-via-class-member-funcptr.cpp | 36 +++++++++++++++++++
 2 files changed, 40 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 52021fce1c675..3f504e5132684 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1820,10 +1820,10 @@ CIRGenCallee CIRGenFunction::emitCallee(const 
clang::Expr *e) {
     // Resolve direct calls.
     const auto *funcDecl = cast<FunctionDecl>(declRef->getDecl());
     return emitDirectCallee(funcDecl);
-  } else if (isa<MemberExpr>(e)) {
-    cgm.errorNYI(e->getSourceRange(),
-                 "emitCallee: call to member function is NYI");
-    return {};
+  } else if (auto me = dyn_cast<MemberExpr>(e)) {
+    const auto *fd = cast<FunctionDecl>(me->getMemberDecl());
+    emitIgnoredExpr(me->getBase());
+    return emitDirectCallee(fd);
   } else if (auto *pde = dyn_cast<CXXPseudoDestructorExpr>(e)) {
     return CIRGenCallee::forPseudoDestructor(pde);
   }
diff --git a/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp 
b/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp
new file mode 100644
index 0000000000000..28f6a2020fd99
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -std=c++20 -triple aarch64-none-linux-android21 -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple aarch64-none-linux-android21 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++20 -triple aarch64-none-linux-android21 -emit-llvm 
%s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+class A {
+public:
+  static char *b(int);
+};
+
+int h=0;
+
+class F {
+public:
+  const char *b();
+  A g;
+};
+
+const char *F::b() { return g.b(h); }
+
+void fn1() { F f1; }
+
+// CIR: cir.func {{.*}} @_ZN1F1bEv
+// CIR:   %[[H_PTR:.*]] = cir.get_global @h : !cir.ptr<!s32i>
+// CIR:   %[[H_VAL:.*]] = cir.load{{.*}} %[[H_PTR]] : !cir.ptr<!s32i>, !s32i
+// CIR:   %[[RET:.*]] = cir.call @_ZN1A1bEi(%[[H_VAL]]) : (!s32i) -> 
!cir.ptr<!s8i>
+
+// LLVM: define {{.*}} ptr @_ZN1F1bEv
+// LLVM:   %[[VAR_H:.*]] = load i32, ptr @h
+// LLVM:   %[[RET:.*]] = call ptr @_ZN1A1bEi(i32 %[[VAR_H]])
+
+// OGCG: define {{.*}} ptr @_ZN1F1bEv
+// OGCG:   %[[VAR_H:.*]] = load i32, ptr @h
+// OGCG:   %[[RET:.*]] = call noundef ptr @_ZN1A1bEi(i32 noundef %[[VAR_H]])

>From 853ebb459f840838df13dffdf37a0951292a1472 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Wed, 22 Oct 2025 13:57:02 -0700
Subject: [PATCH 2/2] Fix handling of call via indirect reference

---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  8 +--
 .../CodeGen/call-via-class-member-funcptr.cpp | 50 +++++++++++++++++++
 2 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 3f504e5132684..f7d004bf15055 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1821,9 +1821,11 @@ CIRGenCallee CIRGenFunction::emitCallee(const 
clang::Expr *e) {
     const auto *funcDecl = cast<FunctionDecl>(declRef->getDecl());
     return emitDirectCallee(funcDecl);
   } else if (auto me = dyn_cast<MemberExpr>(e)) {
-    const auto *fd = cast<FunctionDecl>(me->getMemberDecl());
-    emitIgnoredExpr(me->getBase());
-    return emitDirectCallee(fd);
+    if (const auto *fd = dyn_cast<FunctionDecl>(me->getMemberDecl())) {
+      emitIgnoredExpr(me->getBase());
+      return emitDirectCallee(fd);
+    }
+    // Else fall through to the indirect reference handling below.
   } else if (auto *pde = dyn_cast<CXXPseudoDestructorExpr>(e)) {
     return CIRGenCallee::forPseudoDestructor(pde);
   }
diff --git a/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp 
b/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp
index 28f6a2020fd99..dbde45499a40b 100644
--- a/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp
+++ b/clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp
@@ -34,3 +34,53 @@ void fn1() { F f1; }
 // OGCG: define {{.*}} ptr @_ZN1F1bEv
 // OGCG:   %[[VAR_H:.*]] = load i32, ptr @h
 // OGCG:   %[[RET:.*]] = call noundef ptr @_ZN1A1bEi(i32 noundef %[[VAR_H]])
+
+class B {
+public:
+  B();
+  int (&indirect_callee_int_ref)(int);
+};
+
+class C {
+public:
+  int call_indirect(int v) { return inner.indirect_callee_int_ref(v); };
+  B inner;
+};
+
+void fn2() { C c1; c1.call_indirect(2); }
+
+// CIR: cir.func {{.*}} @_ZN1C13call_indirectEi(%[[THIS_ARG:.*]]: 
!cir.ptr<!rec_C> {{.*}}, %[[V_ARG:.*]]: !s32i {{.*}}) -> !s32i
+// CIR:   %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, 
!cir.ptr<!cir.ptr<!rec_C>>, ["this", init]
+// CIR:   %[[V_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["v", init]
+// CIR:   cir.store %[[THIS_ARG]], %[[THIS_ADDR]]
+// CIR:   cir.store %[[V_ARG]], %[[V_ADDR]]
+// CIR:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
+// CIR:   %[[INNER:.*]] = cir.get_member %[[THIS]][0] {name = "inner"} : 
!cir.ptr<!rec_C> -> !cir.ptr<!rec_B>
+// CIR:   %[[INDIRECT_CALLEE_PTR:.*]] = cir.get_member %[[INNER]][0] {name = 
"indirect_callee_int_ref"}
+// CIR:   %[[INDIRECT_CALLEE:.*]] = cir.load %[[INDIRECT_CALLEE_PTR]]
+// CIR:   %[[V:.*]] = cir.load{{.*}} %[[V_ADDR]] : !cir.ptr<!s32i>, !s32i
+// CIR:   %[[RET:.*]] = cir.call %[[INDIRECT_CALLEE]](%[[V]])
+
+// LLVM: define {{.*}} i32 @_ZN1C13call_indirectEi(ptr %[[THIS_ARG:.*]], i32 
%[[V_ARG:.*]])
+// LLVM:   %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM:   %[[V_ADDR:.*]] = alloca i32
+// LLVM:   store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// LLVM:   store i32 %[[V_ARG]], ptr %[[V_ADDR]]
+// LLVM:   %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// LLVM:   %[[INNER:.*]] = getelementptr %class.C, ptr %[[THIS]], i32 0, i32 0
+// LLVM:   %[[INDIRECT_CALLEE_PTR:.*]] = getelementptr %class.B, ptr 
%[[INNER]], i32 0, i32 0
+// LLVM:   %[[INDIRECT_CALLEE:.*]] = load ptr, ptr %[[INDIRECT_CALLEE_PTR]]
+// LLVM:   %[[V:.*]] = load i32, ptr %[[V_ADDR]]
+// LLVM:   %[[RET:.*]] = call i32 %[[INDIRECT_CALLEE]](i32 %[[V]])
+
+// OGCG: define {{.*}} i32 @_ZN1C13call_indirectEi(ptr {{.*}} 
%[[THIS_ARG:.*]], i32 {{.*}} %[[V_ARG:.*]])
+// OGCG:   %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG:   %[[V_ADDR:.*]] = alloca i32
+// OGCG:   store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
+// OGCG:   store i32 %[[V_ARG]], ptr %[[V_ADDR]]
+// OGCG:   %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// OGCG:   %[[INNER:.*]] = getelementptr inbounds nuw %class.C, ptr %[[THIS]], 
i32 0, i32 0
+// OGCG:   %[[INDIRECT_CALLEE_PTR:.*]] = getelementptr inbounds nuw %class.B, 
ptr %[[INNER]], i32 0, i32 0
+// OGCG:   %[[INDIRECT_CALLEE:.*]] = load ptr, ptr %[[INDIRECT_CALLEE_PTR]]
+// OGCG:   %[[V:.*]] = load i32, ptr %[[V_ADDR]]
+// OGCG:   %[[RET:.*]] = call noundef i32 %[[INDIRECT_CALLEE]](i32 noundef 
%[[V]])

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to