Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGObjCGNU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
101 changes: 77 additions & 24 deletions clang/lib/CodeGen/CGObjCMac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -1053,8 +1063,19 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
GenerateMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD = nullptr) override;

llvm::Function *GenerateDirectMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD);
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,
Expand Down Expand Up @@ -2080,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;
Expand Down Expand Up @@ -3847,7 +3869,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);

Expand All @@ -3863,7 +3887,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();
Expand All @@ -3882,7 +3906,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();
Expand All @@ -3896,20 +3920,42 @@ 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;
Expand All @@ -3926,18 +3972,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
Expand All @@ -3946,17 +3985,18 @@ 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
// of weak linking.
ReceiverCanBeNull = isWeakLinkedClass(OID);
}

// Generate nil check
if (ReceiverCanBeNull) {
llvm::BasicBlock *SelfIsNilBlock =
CGF.createBasicBlock("objc_direct_method.self_is_nil");
Expand Down Expand Up @@ -3986,8 +4026,21 @@ void CGObjCCommonMac::GenerateDirectMethodPrologue(
CGF.EmitBlock(ContBlock);
Builder.SetInsertPoint(ContBlock);
}
}

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);
}

// only synthesize _cmd if it's referenced
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.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CGObjCRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down