[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted created
https://github.com/llvm/llvm-project/pull/170619
In many cases we can infer that class object has been realized
>From 110198ae0bd837972d4d9ff013494fbf3206b7a4 Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 63 +
clang/lib/CodeGen/CGObjCRuntime.h | 6 +--
2 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index 38efd4d865284..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,6 +415,69 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
// Otherwise, assume it can be unrealized.
return true;
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index 8d5ee1310e51f..c49eef3ad30c1 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,
@@ -345,8 +345,8 @@ class CGObjCRuntime {
virtual bool canMessageReceiverBeNull(CodeGenFunction &CGF,
const ObjCMethodDecl *method,
bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+const ObjCInterfaceDecl *classReceiver,
+
[llvm-branch-commits] [clang] [ExposeDirectMethod] Nil chech thunk generation (PR #170618)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170618 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170617 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170619 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeDirectMethod] Nil chech thunk generation (PR #170618)
https://github.com/DataCorrupted created
https://github.com/llvm/llvm-project/pull/170618
- Generation
- Dispatch
>From 81b0d800623f03b0c3fa9b4412e36dc585236ed8 Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:42:51 -0800
Subject: [PATCH] [ExposeDirectMethod] Nil chech thunk generation
- Generation
- Dispatch
---
clang/lib/CodeGen/CGDecl.cpp| 4 +-
clang/lib/CodeGen/CGObjC.cpp| 17 +-
clang/lib/CodeGen/CGObjCMac.cpp | 243 +++-
clang/lib/CodeGen/CodeGenFunction.h | 7 +
4 files changed, 263 insertions(+), 8 deletions(-)
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b1cd83af2396..9f0e09eac8866 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -2757,7 +2757,9 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D,
ParamValue Arg,
llvm::Value *ArgVal = (DoStore ? Arg.getDirectValue() : nullptr);
LValue lv = MakeAddrLValue(DeclPtr, Ty);
- if (IsScalar) {
+ // If this is a thunk, don't bother with ARC lifetime management.
+ // The true implementation will take care of that.
+ if (IsScalar && !CurFuncIsThunk) {
Qualifiers qs = Ty.getQualifiers();
if (Qualifiers::ObjCLifetime lt = qs.getObjCLifetime()) {
// We honor __attribute__((ns_consumed)) for types with lifetime.
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
index 10aad2e26938d..f1b8627fc119a 100644
--- a/clang/lib/CodeGen/CGObjC.cpp
+++ b/clang/lib/CodeGen/CGObjC.cpp
@@ -761,7 +761,18 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl
*OMD,
const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD);
if (OMD->isDirectMethod()) {
-Fn->setVisibility(llvm::Function::HiddenVisibility);
+Fn->setVisibility(llvm::GlobalValue::HiddenVisibility);
+if (CGM.shouldExposeSymbol(OMD)) {
+ // Find the decl that may have visibility set (property or method)
+ const NamedDecl *Decl = OMD;
+ if (const auto *PD = OMD->findPropertyDecl()) {
+Decl = PD;
+ }
+ // and respect source level visibility setting
+ if (auto V = Decl->getExplicitVisibility(NamedDecl::VisibilityForValue))
{
+Fn->setVisibility(CGM.GetLLVMVisibility(*V));
+ }
+}
CGM.SetLLVMFunctionAttributes(OMD, FI, Fn, /*IsThunk=*/false);
CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn);
} else {
@@ -781,10 +792,6 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl
*OMD,
OMD->getLocation(), StartLoc);
if (OMD->isDirectMethod()) {
-// This function is a direct call, it has to implement a nil check
-// on entry.
-//
-// TODO: possibly have several entry points to elide the check
CGM.getObjCRuntime().GenerateDirectMethodPrologue(*this, Fn, OMD, CD);
}
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index 3f4b11c634ce4..741e5d85b5935 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -1066,6 +1066,15 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
DirectMethodInfo &GenerateDirectMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD);
+ llvm::Function *GenerateObjCDirectThunk(const ObjCMethodDecl *OMD,
+ const ObjCContainerDecl *CD,
+ llvm::Function *Implementation);
+
+ llvm::Function *GetDirectMethodCallee(const ObjCMethodDecl *OMD,
+const ObjCContainerDecl *CD,
+bool ReceiverCanBeNull,
+bool ClassObjectCanBeUnrealized);
+
/// Generate class realization code: [self self]
/// This is used for class methods to ensure the class is initialized.
/// Returns the realized class object.
@@ -2094,6 +2103,9 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
bool ReceiverCanBeNull =
canMessageReceiverBeNull(CGF, Method, IsSuper, ClassReceiver, Arg0);
+ bool ClassObjectCanBeUnrealized =
+ Method && Method->isClassMethod() &&
+ canClassObjectBeUnrealized(ClassReceiver, CGF);
bool RequiresNullCheck = false;
bool RequiresSelValue = true;
@@ -2101,7 +2113,11 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(
llvm::FunctionCallee Fn = nullptr;
if (Method && Method->isDirectMethod()) {
assert(!IsSuper);
-Fn = GenerateDirectMethod(Method, Method->getClassInterface());
+// Use GetDirectMethodCallee to decide whether to use implementation or
+// thunk.
+Fn = GetDirectMethodCallee(Method, Method->getClassInterface(),
+ ReceiverCanBeNull, ClassObjectCanBeUnrealized);
+
// Direct methods will synthesize the proper `_cmd` internally,
// so just don't bother with setting the `_cmd` argument.
RequiresSelValue = false;
@@ -2138,6 +2154,23 @@
[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
https://github.com/DataCorrupted created
https://github.com/llvm/llvm-project/pull/170617
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
>From ff65d5543f5187998bf7a35017e70c5605008739 Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:35:15 -0800
Subject: [PATCH] [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
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 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
+ llvm::DenseMap
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::Gener
[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170617 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted updated
https://github.com/llvm/llvm-project/pull/170619
>From bbf2e85a9bc07a52c83d13af5db0d35878484b9a Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH 1/8] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 65 -
clang/lib/CodeGen/CGObjCRuntime.h | 23 +++---
2 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a4b4460fdc49c..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,7 +415,70 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
- // TODO
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
+
// Otherwise, assume it can be unrealized.
return true;
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..b0cf04fc8553b 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,
@@ -330,10 +330,23 @@ class CGObjCRuntime {
QualType resultType,
CallArgList &callArgs);
- bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-const ObjCMethodDecl *method, bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+ /// Check if the receiver of an ObjC message send can be null.
+
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted updated
https://github.com/llvm/llvm-project/pull/170619
>From bbf2e85a9bc07a52c83d13af5db0d35878484b9a Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH 1/3] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 65 -
clang/lib/CodeGen/CGObjCRuntime.h | 23 +++---
2 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a4b4460fdc49c..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,7 +415,70 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
- // TODO
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
+
// Otherwise, assume it can be unrealized.
return true;
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..b0cf04fc8553b 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,
@@ -330,10 +330,23 @@ class CGObjCRuntime {
QualType resultType,
CallArgList &callArgs);
- bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-const ObjCMethodDecl *method, bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+ /// Check if the receiver of an ObjC message send can be null.
+
[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
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
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
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 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
+ llvm::DenseMap
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 *C
[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
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
Date: Wed, 3 Dec 2025 22:35:15 -0800
Subject: [PATCH 1/2] [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
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 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
+ llvm::DenseMap
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 *C
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170619 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted updated
https://github.com/llvm/llvm-project/pull/170619
>From bbf2e85a9bc07a52c83d13af5db0d35878484b9a Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH 1/4] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 65 -
clang/lib/CodeGen/CGObjCRuntime.h | 23 +++---
2 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a4b4460fdc49c..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,7 +415,70 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
- // TODO
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
+
// Otherwise, assume it can be unrealized.
return true;
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..b0cf04fc8553b 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,
@@ -330,10 +330,23 @@ class CGObjCRuntime {
QualType resultType,
CallArgList &callArgs);
- bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-const ObjCMethodDecl *method, bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+ /// Check if the receiver of an ObjC message send can be null.
+
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170619 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170619 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted updated
https://github.com/llvm/llvm-project/pull/170619
>From bbf2e85a9bc07a52c83d13af5db0d35878484b9a Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH 1/7] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 65 -
clang/lib/CodeGen/CGObjCRuntime.h | 23 +++---
2 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a4b4460fdc49c..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,7 +415,70 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
- // TODO
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
+
// Otherwise, assume it can be unrealized.
return true;
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..b0cf04fc8553b 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,
@@ -330,10 +330,23 @@ class CGObjCRuntime {
QualType resultType,
CallArgList &callArgs);
- bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-const ObjCMethodDecl *method, bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+ /// Check if the receiver of an ObjC message send can be null.
+
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted updated
https://github.com/llvm/llvm-project/pull/170619
>From bbf2e85a9bc07a52c83d13af5db0d35878484b9a Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH 1/5] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 65 -
clang/lib/CodeGen/CGObjCRuntime.h | 23 +++---
2 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a4b4460fdc49c..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,7 +415,70 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
- // TODO
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
+
// Otherwise, assume it can be unrealized.
return true;
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..b0cf04fc8553b 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,
@@ -330,10 +330,23 @@ class CGObjCRuntime {
QualType resultType,
CallArgList &callArgs);
- bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-const ObjCMethodDecl *method, bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+ /// Check if the receiver of an ObjC message send can be null.
+
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted updated
https://github.com/llvm/llvm-project/pull/170619
>From bbf2e85a9bc07a52c83d13af5db0d35878484b9a Mon Sep 17 00:00:00 2001
From: Peter Rong
Date: Wed, 3 Dec 2025 22:45:04 -0800
Subject: [PATCH 1/9] [ExposeObjCDirect] Optimizations
In many cases we can infer that class object has been realized
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 65 -
clang/lib/CodeGen/CGObjCRuntime.h | 23 +++---
2 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a4b4460fdc49c..fd227d9645ac1 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -415,7 +415,70 @@ bool CGObjCRuntime::canMessageReceiverBeNull(
bool CGObjCRuntime::canClassObjectBeUnrealized(
const ObjCInterfaceDecl *CalleeClassDecl, CodeGenFunction &CGF) const {
- // TODO
+ if (!CalleeClassDecl)
+return true;
+
+ // Heuristic 1: +load method on this class
+ // If the class has a +load method, it's realized when the binary is loaded.
+ ASTContext &Ctx = CGM.getContext();
+ const IdentifierInfo *LoadII = &Ctx.Idents.get("load");
+ Selector LoadSel = Ctx.Selectors.getSelector(0, &LoadII);
+
+ // TODO: if one if the child had +load, this class is guaranteed to be
+ // realized as well. We should have a translation unit specific map that
+ // precomputes all classes that are realized, and just do a lookup here.
+ // But we need to measure how expensive it is to create a map like that.
+ if (CalleeClassDecl->lookupClassMethod(LoadSel))
+return false; // This class has +load, so it's already realized
+
+ // Heuristic 2: using Self / Super
+ // If we're currently executing a method of ClassDecl (or a subclass),
+ // then ClassDecl must already be realized.
+ if (const auto *CurMethod =
+ dyn_cast_or_null(CGF.CurCodeDecl)) {
+const ObjCInterfaceDecl *CallerCalssDecl = CurMethod->getClassInterface();
+if (CallerCalssDecl && CalleeClassDecl->isSuperClassOf(CallerCalssDecl))
+ return false;
+ }
+
+ // Heuristic 3: previously realized
+ // Heuristic 3.1: Walk through the current BasicBlock looking for calls that
+ // realize the class. All heuristics in this cluster share the same
+ // implementation pattern.
+ auto *BB = CGF.Builder.GetInsertBlock();
+ if (!BB)
+return true; // No current block, assume unrealized
+
+ llvm::StringRef CalleeClassName = CalleeClassDecl->getName();
+
+ // Heuristic 3.2 / TODO: If realization happened in a dominating block, the
+ // class is realized Requires Dominator tree analysis. There should be an
+ // outer loop `for (BB: DominatingBasicBlocks)`
+ for (const auto &Inst : *BB) {
+// Check if this is a call instruction
+const auto *Call = llvm::dyn_cast(&Inst);
+if (!Call)
+ continue;
+llvm::Function *CalledFunc = Call->getCalledFunction();
+if (!CalledFunc)
+ continue;
+
+llvm::StringRef FuncNamePtr = CalledFunc->getName();
+// Skip the \01 prefix if present
+if (FuncNamePtr.starts_with("\01"))
+ FuncNamePtr = FuncNamePtr.drop_front(1);
+// Check for instance method calls: "-[ClassName methodName]"
+// or class method calls: "+[ClassName methodName]"
+// Also check for thunks: "-[ClassName methodName]_thunk"
+if ((FuncNamePtr.starts_with("-[") || FuncNamePtr.starts_with("+["))) {
+ FuncNamePtr = FuncNamePtr.drop_front(2);
+ // TODO: if the current class is the super class of the function that's
+ // used, it should've been realized as well
+ if (FuncNamePtr.starts_with(CalleeClassName))
+return false;
+}
+ }
+
// Otherwise, assume it can be unrealized.
return true;
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h
b/clang/lib/CodeGen/CGObjCRuntime.h
index d3d4745cb77a7..b0cf04fc8553b 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,
@@ -330,10 +330,23 @@ class CGObjCRuntime {
QualType resultType,
CallArgList &callArgs);
- bool canMessageReceiverBeNull(CodeGenFunction &CGF,
-const ObjCMethodDecl *method, bool isSuper,
-const ObjCInterfaceDecl *classReceiver,
-llvm::Value *receiver);
+ /// Check if the receiver of an ObjC message send can be null.
+
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted edited https://github.com/llvm/llvm-project/pull/170619 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Optimizations (PR #170619)
https://github.com/DataCorrupted ready_for_review https://github.com/llvm/llvm-project/pull/170619 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [ExposeObjCDirect] Setup helper functions (PR #170617)
https://github.com/DataCorrupted ready_for_review https://github.com/llvm/llvm-project/pull/170617 ___ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
