[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
Peter Rong via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Dec 4 11:57:39 PST 2025
https://github.com/DataCorrupted updated https://github.com/llvm/llvm-project/pull/170617
>From fb969c3e8f50f80f497ab6b1aca23537e04d172b Mon Sep 17 00:00:00 2001
From: Peter Rong <PeterRong at meta.com>
Date: Wed, 3 Dec 2025 22:35:15 -0800
Subject: [PATCH 1/3] [ExposeObjCDirect] Setup helper functions
1. GenerateDirectMethodsPreconditionCheck: Move some functionalities to a separate functions.
Those functions will be reused if we move precondition checks into a thunk
2. Create `DirectMethodInfo`, which will be used to manage true implementation and its thunk
---
clang/lib/CodeGen/CGObjCGNU.cpp | 9 +++
clang/lib/CodeGen/CGObjCMac.cpp | 95 ++++++++++++++++++++++++-------
clang/lib/CodeGen/CGObjCRuntime.h | 6 ++
3 files changed, 88 insertions(+), 22 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp
index 06643d4bdc211..9c814487860ac 100644
--- a/clang/lib/CodeGen/CGObjCGNU.cpp
+++ b/clang/lib/CodeGen/CGObjCGNU.cpp
@@ -600,6 +600,9 @@ class CGObjCGNU : public CGObjCRuntime {
// Map to unify direct method definitions.
llvm::DenseMap<const ObjCMethodDecl *, llvm::Function *>
DirectMethodDefinitions;
+ void GenerateDirectMethodsPreconditionCheck(
+ CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
+ const ObjCContainerDecl *CD) override;
void GenerateDirectMethodPrologue(CodeGenFunction &CGF, llvm::Function *Fn,
const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) override;
@@ -4196,6 +4199,12 @@ llvm::Function *CGObjCGNU::GenerateMethod(const ObjCMethodDecl *OMD,
return Fn;
}
+void CGObjCGNU::GenerateDirectMethodsPreconditionCheck(
+ CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
+ const ObjCContainerDecl *CD) {
+ // GNU runtime doesn't support direct calls at this time
+}
+
void CGObjCGNU::GenerateDirectMethodPrologue(CodeGenFunction &CGF,
llvm::Function *Fn,
const ObjCMethodDecl *OMD,
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index cb5bb403bb53b..3f4b11c634ce4 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -847,9 +847,19 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
/// this translation unit.
llvm::DenseMap<const ObjCMethodDecl *, llvm::Function *> MethodDefinitions;
+ /// Information about a direct method definition
+ struct DirectMethodInfo {
+ llvm::Function
+ *Implementation; // The true implementation (where body is emitted)
+ llvm::Function *Thunk; // The nil-check thunk (nullptr if not generated)
+
+ DirectMethodInfo(llvm::Function *Impl, llvm::Function *Thunk = nullptr)
+ : Implementation(Impl), Thunk(Thunk) {}
+ };
+
/// DirectMethodDefinitions - map of direct methods which have been defined in
/// this translation unit.
- llvm::DenseMap<const ObjCMethodDecl *, llvm::Function *>
+ llvm::DenseMap<const ObjCMethodDecl *, DirectMethodInfo>
DirectMethodDefinitions;
/// PropertyNames - uniqued method variable names.
@@ -1053,9 +1063,20 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
GenerateMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD = nullptr) override;
- llvm::Function *GenerateDirectMethod(const ObjCMethodDecl *OMD,
+ DirectMethodInfo &GenerateDirectMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD);
+ /// Generate class realization code: [self self]
+ /// This is used for class methods to ensure the class is initialized.
+ /// Returns the realized class object.
+ llvm::Value *GenerateClassRealization(CodeGenFunction &CGF,
+ llvm::Value *classObject,
+ const ObjCInterfaceDecl *OID);
+
+ void GenerateDirectMethodsPreconditionCheck(
+ CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
+ const ObjCContainerDecl *CD) override;
+
void GenerateDirectMethodPrologue(CodeGenFunction &CGF, llvm::Function *Fn,
const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) override;
@@ -3847,7 +3868,9 @@ llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD,
llvm::Function *Method;
if (OMD->isDirectMethod()) {
- Method = GenerateDirectMethod(OMD, CD);
+ // Returns DirectMethodInfo& containing both Implementation and Thunk
+ DirectMethodInfo &Info = GenerateDirectMethod(OMD, CD);
+ Method = Info.Implementation; // Extract implementation for body generation
} else {
auto Name = getSymbolNameForMethod(OMD);
@@ -3863,7 +3886,7 @@ llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD,
return Method;
}
-llvm::Function *
+CGObjCCommonMac::DirectMethodInfo &
CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) {
auto *COMD = OMD->getCanonicalDecl();
@@ -3882,7 +3905,7 @@ CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD,
// a new one that has the proper type below.
if (!OMD->getBody() || COMD->getReturnType() == OMD->getReturnType())
return I->second;
- OldFn = I->second;
+ OldFn = I->second.Implementation;
}
CodeGenTypes &Types = CGM.getTypes();
@@ -3896,20 +3919,41 @@ CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD,
OldFn->replaceAllUsesWith(Fn);
OldFn->eraseFromParent();
- // Replace the cached function in the map.
- I->second = Fn;
+ // Replace the cached implementation in the map.
+ I->second.Implementation = Fn;
+
} else {
auto Name = getSymbolNameForMethod(OMD, /*include category*/ false);
Fn = llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage,
Name, &CGM.getModule());
- DirectMethodDefinitions.insert(std::make_pair(COMD, Fn));
+ auto [It, inserted] = DirectMethodDefinitions.insert(std::make_pair(COMD, DirectMethodInfo(Fn)));
+ I = It;
}
- return Fn;
+ // Return reference to DirectMethodInfo (contains both Implementation and
+ // Thunk)
+ return I->second;
}
-void CGObjCCommonMac::GenerateDirectMethodPrologue(
+llvm::Value *
+CGObjCCommonMac::GenerateClassRealization(CodeGenFunction &CGF,
+ llvm::Value *classObject,
+ const ObjCInterfaceDecl *OID) {
+ // Generate: self = [self self]
+ // This forces class lazy initialization
+ Selector SelfSel = GetNullarySelector("self", CGM.getContext());
+ auto ResultType = CGF.getContext().getObjCIdType();
+ CallArgList Args;
+
+ RValue result = GeneratePossiblySpecializedMessageSend(
+ CGF, ReturnValueSlot(), ResultType, SelfSel, classObject, Args, OID,
+ nullptr, true);
+
+ return result.getScalarVal();
+}
+
+void CGObjCCommonMac::GenerateDirectMethodsPreconditionCheck(
CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) {
auto &Builder = CGF.Builder;
@@ -3926,18 +3970,11 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
// if (self == nil) {
// return (ReturnType){ };
// }
- //
- // _cmd = @selector(...)
- // ...
if (OMD->isClassMethod()) {
const ObjCInterfaceDecl *OID = cast<ObjCInterfaceDecl>(CD);
assert(OID &&
"GenerateDirectMethod() should be called with the Class Interface");
- Selector SelfSel = GetNullarySelector("self", CGM.getContext());
- auto ResultType = CGF.getContext().getObjCIdType();
- RValue result;
- CallArgList Args;
// TODO: If this method is inlined, the caller might know that `self` is
// already initialized; for example, it might be an ordinary Objective-C
@@ -3946,10 +3983,10 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
//
// We should find a way to eliminate this unnecessary initialization in such
// cases in LLVM.
- result = GeneratePossiblySpecializedMessageSend(
- CGF, ReturnValueSlot(), ResultType, SelfSel, selfValue, Args, OID,
- nullptr, true);
- Builder.CreateStore(result.getScalarVal(), selfAddr);
+
+ // Perform class realization using the helper function
+ llvm::Value *realizedClass = GenerateClassRealization(CGF, selfValue, OID);
+ Builder.CreateStore(realizedClass, selfAddr);
// Nullable `Class` expressions cannot be messaged with a direct method
// so the only reason why the receive can be null would be because
@@ -3957,6 +3994,7 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
ReceiverCanBeNull = isWeakLinkedClass(OID);
}
+ // Generate nil check
if (ReceiverCanBeNull) {
llvm::BasicBlock *SelfIsNilBlock =
CGF.createBasicBlock("objc_direct_method.self_is_nil");
@@ -3986,8 +4024,21 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
CGF.EmitBlock(ContBlock);
Builder.SetInsertPoint(ContBlock);
}
+}
- // only synthesize _cmd if it's referenced
+void CGObjCCommonMac::GenerateDirectMethodPrologue(
+ CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
+ const ObjCContainerDecl *CD) {
+ // Generate precondition checks (class realization + nil check) if needed
+ // Without flag: precondition checks are in the implementation
+ // With flag: precondition checks will be in the thunk (not here)
+ if (!CGM.shouldExposeSymbol(OMD)) {
+ GenerateDirectMethodsPreconditionCheck(CGF, Fn, OMD, CD);
+ }
+
+ auto &Builder = CGF.Builder;
+ // Only synthesize _cmd if it's referenced
+ // This is the actual "prologue" work that always happens
if (OMD->getCmdDecl()->isUsed()) {
// `_cmd` is not a parameter to direct methods, so storage must be
// explicitly declared for it.
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h
index 0f0cc6ba09200..d3d4745cb77a7 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.h
+++ b/clang/lib/CodeGen/CGObjCRuntime.h
@@ -226,6 +226,12 @@ class CGObjCRuntime {
virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) = 0;
+/// Generates precondition checks for direct Objective-C Methods.
+ /// This includes [self self] for class methods and nil checks.
+ virtual void GenerateDirectMethodsPreconditionCheck(
+ CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
+ const ObjCContainerDecl *CD) = 0;
+
/// Generates prologue for direct Objective-C Methods.
virtual void GenerateDirectMethodPrologue(CodeGenFunction &CGF,
llvm::Function *Fn,
>From b6ddc7acecb1119626dce2c07b1d979fca989cee Mon Sep 17 00:00:00 2001
From: Peter Rong <PeterRong at meta.com>
Date: Thu, 4 Dec 2025 09:59:36 -0800
Subject: [PATCH 2/3] fix error
---
clang/lib/CodeGen/CGObjCMac.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index 3f4b11c634ce4..e557f3543e240 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -2101,7 +2101,8 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
llvm::FunctionCallee Fn = nullptr;
if (Method && Method->isDirectMethod()) {
assert(!IsSuper);
- Fn = GenerateDirectMethod(Method, Method->getClassInterface());
+ auto Info = GenerateDirectMethod(Method, Method->getClassInterface());
+ Fn = Info.Implementation;
// Direct methods will synthesize the proper `_cmd` internally,
// so just don't bother with setting the `_cmd` argument.
RequiresSelValue = false;
>From 8dfa71635023b7d0bd349f94cff0a569ac8dfa65 Mon Sep 17 00:00:00 2001
From: Peter Rong <PeterRong at meta.com>
Date: Thu, 4 Dec 2025 11:57:11 -0800
Subject: [PATCH 3/3] format
---
clang/lib/CodeGen/CGObjCMac.cpp | 7 ++++---
clang/lib/CodeGen/CGObjCRuntime.h | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index e557f3543e240..edc6c19f2c227 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -1064,7 +1064,7 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
const ObjCContainerDecl *CD = nullptr) override;
DirectMethodInfo &GenerateDirectMethod(const ObjCMethodDecl *OMD,
- const ObjCContainerDecl *CD);
+ const ObjCContainerDecl *CD);
/// Generate class realization code: [self self]
/// This is used for class methods to ensure the class is initialized.
@@ -3928,7 +3928,8 @@ CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD,
Fn = llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage,
Name, &CGM.getModule());
- auto [It, inserted] = DirectMethodDefinitions.insert(std::make_pair(COMD, DirectMethodInfo(Fn)));
+ auto [It, inserted] = DirectMethodDefinitions.insert(
+ std::make_pair(COMD, DirectMethodInfo(Fn)));
I = It;
}
@@ -4036,7 +4037,7 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
if (!CGM.shouldExposeSymbol(OMD)) {
GenerateDirectMethodsPreconditionCheck(CGF, Fn, OMD, CD);
}
-
+
auto &Builder = CGF.Builder;
// Only synthesize _cmd if it's referenced
// This is the actual "prologue" work that always happens
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..a1df6b1c5281a 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.h
+++ b/clang/lib/CodeGen/CGObjCRuntime.h
@@ -226,7 +226,7 @@ class CGObjCRuntime {
virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) = 0;
-/// Generates precondition checks for direct Objective-C Methods.
+ /// Generates precondition checks for direct Objective-C Methods.
/// This includes [self self] for class methods and nil checks.
virtual void GenerateDirectMethodsPreconditionCheck(
CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
More information about the llvm-branch-commits
mailing list