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 <[email protected]> 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<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 1ee3b85e8a779..8d5ee1310e51f 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, _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
