From: Daniel Dunbar Date: Mon, 25 Aug 2008 08:19:24 +0000 (+0000) Subject: Fix Obj-C super sends inside class methods. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f56f1913e91ad32bed52dd3f6afc26735d336584;p=clang Fix Obj-C super sends inside class methods. - NeXT loads the super class at runtime; this required changing the runtime interface to pass more information down. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@55307 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index b086f4f10c..ddd7c8119b 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -50,6 +50,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E) { CGObjCRuntime &Runtime = CGM.getObjCRuntime(); const Expr *ReceiverExpr = E->getReceiver(); bool isSuperMessage = false; + bool isClassMessage = false; // Find the receiver llvm::Value *Receiver; if (!ReceiverExpr) { @@ -60,11 +61,13 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E) { if (!OID) { assert(!strcmp(E->getClassName()->getName(), "super") && "Unexpected missing class interface in message send."); - OID = E->getMethodDecl()->getClassInterface(); isSuperMessage = true; + Receiver = LoadObjCSelf(); + } else { + Receiver = Runtime.GetClass(Builder, OID); } - - Receiver = Runtime.GetClass(Builder, OID); + + isClassMessage = true; } else if (const PredefinedExpr *PDE = dyn_cast(E->getReceiver())) { assert(PDE->getIdentType() == PredefinedExpr::ObjCSuper); @@ -78,10 +81,11 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E) { // super is only valid in an Objective-C method const ObjCMethodDecl *OMD = cast(CurFuncDecl); return Runtime.GenerateMessageSendSuper(*this, E, - OMD->getClassInterface()->getSuperClass(), - Receiver); + OMD->getClassInterface(), + Receiver, + isClassMessage); } - return Runtime.GenerateMessageSend(*this, E, Receiver); + return Runtime.GenerateMessageSend(*this, E, Receiver, isClassMessage); } /// Generate an Objective-C method. An Objective-C method is a C function with diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index b64f365832..1b254c0a26 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -97,12 +97,14 @@ public: virtual CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - llvm::Value *Receiver); + llvm::Value *Receiver, + bool IsClassMessage); virtual CodeGen::RValue GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - const ObjCInterfaceDecl *Super, - llvm::Value *Receiver); + const ObjCInterfaceDecl *Class, + llvm::Value *Receiver, + bool IsClassMessage); virtual llvm::Value *GetClass(llvm::IRBuilder<> &Builder, const ObjCInterfaceDecl *OID); virtual llvm::Value *GetSelector(llvm::IRBuilder<> &Builder, Selector Sel); @@ -236,8 +238,10 @@ llvm::Constant *CGObjCGNU::GenerateConstantString(const std::string &Str) { CodeGen::RValue CGObjCGNU::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - const ObjCInterfaceDecl *SuperClass, - llvm::Value *Receiver) { + const ObjCInterfaceDecl *Class, + llvm::Value *Receiver, + bool IsClassMessage) { + const ObjCInterfaceDecl *SuperClass = Class->getSuperClass(); const llvm::Type *ReturnTy = CGM.getTypes().ConvertType(E->getType()); // TODO: This should be cached, not looked up every time. llvm::Value *ReceiverClass = GetClass(CGF.Builder, SuperClass); @@ -280,7 +284,8 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, CodeGen::RValue CGObjCGNU::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - llvm::Value *Receiver) { + llvm::Value *Receiver, + bool IsClassMessage) { const llvm::Type *ReturnTy = CGM.getTypes().ConvertType(E->getType()); llvm::Value *cmd = GetSelector(CGF.Builder, E->getSelector()); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 518c574d02..4223709786 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -220,9 +220,14 @@ private: llvm::Constant *EmitIvarList(const ObjCImplementationDecl *ID, bool ForClass, const llvm::Type *InterfaceTy); - + + /// EmitMetaClass - Emit a forward reference to the class structure + /// for the metaclass of the given interface. The return value has + /// type ClassPtrTy. + llvm::Constant *EmitMetaClassRef(const ObjCInterfaceDecl *ID); + /// EmitMetaClass - Emit a class structure for the metaclass of the - /// given implementation. return value has type ClassPtrTy. + /// given implementation. The return value has type ClassPtrTy. llvm::Constant *EmitMetaClass(const ObjCImplementationDecl *ID, llvm::Constant *Protocols, const llvm::Type *InterfaceTy); @@ -316,13 +321,15 @@ public: virtual CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - llvm::Value *Receiver); + llvm::Value *Receiver, + bool IsClassMessage); virtual CodeGen::RValue GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - const ObjCInterfaceDecl *SuperClass, - llvm::Value *Receiver); + const ObjCInterfaceDecl *Class, + llvm::Value *Receiver, + bool IsClassMessage); virtual llvm::Value *GetClass(llvm::IRBuilder<> &Builder, const ObjCInterfaceDecl *ID); @@ -403,12 +410,9 @@ llvm::Constant *CGObjCMac::GenerateConstantString(const std::string &String) { CodeGen::RValue CGObjCMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - const ObjCInterfaceDecl *SuperClass, - llvm::Value *Receiver) { - // FIXME: This should be cached, not looked up every time. Meh. We - // should just make sure the optimizer hits it. - llvm::Value *ReceiverClass = EmitClassRef(CGF.Builder, SuperClass); - + const ObjCInterfaceDecl *Class, + llvm::Value *Receiver, + bool IsClassMessage) { // Create and init a super structure; this is a (receiver, class) // pair we will pass to objc_msgSendSuper. llvm::Value *ObjCSuper = @@ -417,16 +421,28 @@ CGObjCMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, CGF.Builder.CreateBitCast(Receiver, ObjCTypes.ObjectPtrTy); CGF.Builder.CreateStore(ReceiverAsObject, CGF.Builder.CreateStructGEP(ObjCSuper, 0)); - CGF.Builder.CreateStore(ReceiverClass, - CGF.Builder.CreateStructGEP(ObjCSuper, 1)); + // If this is a class message the metaclass is passed as the target. + llvm::Value *Target; + if (IsClassMessage) { + llvm::Value *MetaClassPtr = EmitMetaClassRef(Class); + llvm::Value *SuperPtr = CGF.Builder.CreateStructGEP(MetaClassPtr, 1); + llvm::Value *Super = CGF.Builder.CreateLoad(SuperPtr); + Target = Super; + } else { + Target = EmitClassRef(CGF.Builder, Class->getSuperClass()); + } + CGF.Builder.CreateStore(Target, + CGF.Builder.CreateStructGEP(ObjCSuper, 1)); + return EmitMessageSend(CGF, E, ObjCSuper, true); } /// Generate code for a message send expression. CodeGen::RValue CGObjCMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - llvm::Value *Receiver) { + llvm::Value *Receiver, + bool IsClassMessage) { llvm::Value *Arg0 = CGF.Builder.CreateBitCast(Receiver, ObjCTypes.ObjectPtrTy, "tmp"); return EmitMessageSend(CGF, E, Arg0, false); @@ -972,12 +988,22 @@ llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID, llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassTy, Values); - llvm::GlobalVariable *GV = - new llvm::GlobalVariable(ObjCTypes.ClassTy, false, - llvm::GlobalValue::InternalLinkage, - Init, - std::string("\01L_OBJC_METACLASS_")+ClassName, - &CGM.getModule()); + std::string Name("\01L_OBJC_METACLASS_"); + Name += ClassName; + + // Check for a forward reference. + llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name); + if (GV) { + assert(GV->getType()->getElementType() == ObjCTypes.ClassTy && + "Forward metaclass reference has incorrect type."); + GV->setLinkage(llvm::GlobalValue::InternalLinkage); + GV->setInitializer(Init); + } else { + GV = new llvm::GlobalVariable(ObjCTypes.ClassTy, false, + llvm::GlobalValue::InternalLinkage, + Init, Name, + &CGM.getModule()); + } GV->setSection("__OBJC,__meta_class,regular,no_dead_strip"); UsedGlobals.push_back(GV); // FIXME: Why? @@ -986,6 +1012,31 @@ llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID, return GV; } +llvm::Constant *CGObjCMac::EmitMetaClassRef(const ObjCInterfaceDecl *ID) { + std::string Name("\01L_OBJC_METACLASS_"); + Name += ID->getName(); + + // FIXME: Should we look these up somewhere other than the + // module. Its a bit silly since we only generate these while + // processing an implementation, so exactly one pointer would work + // if know when we entered/exitted an implementation block. + + // Check for an existing forward reference. + if (llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name)) { + assert(GV->getType()->getElementType() == ObjCTypes.ClassTy && + "Forward metaclass reference has incorrect type."); + return GV; + } else { + // Generate as an external reference to keep a consistent + // module. This will be patched up when we emit the metaclass. + return new llvm::GlobalVariable(ObjCTypes.ClassTy, false, + llvm::GlobalValue::ExternalLinkage, + 0, + Name, + &CGM.getModule()); + } +} + /* struct objc_class_ext { uint32_t size; diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index 4a1acda546..e373ef20a7 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/lib/CodeGen/CGObjCRuntime.h @@ -80,15 +80,18 @@ public: virtual CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - llvm::Value *Receiver) = 0; + llvm::Value *Receiver, + bool IsClassMessage) = 0; /// Generate an Objective-C message send operation to the super - /// class. + /// class initiated in a method for Class and with the given Self + /// object. virtual CodeGen::RValue GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, const ObjCMessageExpr *E, - const ObjCInterfaceDecl *SuperClass, - llvm::Value *Receiver) = 0; + const ObjCInterfaceDecl *Class, + llvm::Value *Self, + bool IsClassMessage) = 0; /// Emit the code to return the named protocol as an object, as in a /// @protocol expression.