Skip to content

Conversation

@andykaylor
Copy link
Contributor

This adds support for calling functions via class member access expressions.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Oct 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 21, 2025

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

Changes

This adds support for calling functions via class member access expressions.


Full diff: https://github.com/llvm/llvm-project/pull/164518.diff

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+4-4)
  • (added) clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp (+36)
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]]) 
@llvmbot
Copy link
Member

llvmbot commented Oct 21, 2025

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

This adds support for calling functions via class member access expressions.


Full diff: https://github.com/llvm/llvm-project/pull/164518.diff

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+4-4)
  • (added) clang/test/CIR/CodeGen/call-via-class-member-funcptr.cpp (+36)
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]]) 
"emitCallee: call to member function is NYI");
return {};
} else if (auto me = dyn_cast<MemberExpr>(e)) {
const auto *fd = cast<FunctionDecl>(me->getMemberDecl());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classic-codegen only does this (I think? CGExpr.cpp~6289) IF the cast succeeds (and its a dyn_cast). Do we have reason to think that this is in error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that, and the incubator does the same thing, but if the dyn_cast returns null, both just fall through to the indirect reference handling, and I couldn't find a case where that actually happens so I wasn't confident that it was the right thing to do. We made the same change (if (dyn_cast...) -> cast) just above this in the DeclRefExpr handling.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrmph.... that is concerning. And yeah, the dyn_cast behavior doesn't look like it does anything sensible too.

Can we have a comment here explaining this, so that next-guy through realizes we diverged (in case this becomes a problem)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wrong. I found a way to make the cast fail.

@andykaylor andykaylor merged commit 91d666b into llvm:main Oct 22, 2025
10 checks passed
@andykaylor andykaylor deleted the cir-call-memberexpr branch October 22, 2025 23:31
mikolaj-pirog pushed a commit to mikolaj-pirog/llvm-project that referenced this pull request Oct 23, 2025
…lvm#164518) This adds support for calling functions via class member access expressions.
dvbuka pushed a commit to dvbuka/llvm-project that referenced this pull request Oct 27, 2025
…lvm#164518) This adds support for calling functions via class member access expressions.
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
…lvm#164518) This adds support for calling functions via class member access expressions.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
…lvm#164518) This adds support for calling functions via class member access expressions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

3 participants