From 20ff3108fcd2c3bd734dc79efc22ebaa090abd41 Mon Sep 17 00:00:00 2001 From: Anton Korobeynikov Date: Sun, 1 Jun 2008 14:13:53 +0000 Subject: [PATCH] Support for code generation of Objective-C top-level language constructs. Implemented by David Chisnall! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@51835 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGExprScalar.cpp | 2 +- lib/CodeGen/CGObjCEtoile.cpp | 11 +- lib/CodeGen/CGObjCGNU.cpp | 791 ++++++++++++++++++++++++++++++-- lib/CodeGen/CGObjCRuntime.h | 70 ++- lib/CodeGen/CodeGenFunction.cpp | 21 +- lib/CodeGen/CodeGenModule.cpp | 152 +++++- lib/CodeGen/CodeGenModule.h | 6 + lib/CodeGen/ModuleBuilder.cpp | 18 + 8 files changed, 1027 insertions(+), 44 deletions(-) diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index fc8639dbf2..622d2c5317 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -495,7 +495,7 @@ Value *ScalarExprEmitter::VisitObjCMessageExpr(ObjCMessageExpr *E) { llvm::Constant *Selector = CGF.CGM.GetAddrOfConstantString(SelStr); llvm::Value *SelPtr = Builder.CreateStructGEP(Selector, 0); - return Runtime->generateMessageSend(Builder, ConvertType(E->getType()), + return Runtime->GenerateMessageSend(Builder, ConvertType(E->getType()), CGF.LoadObjCSelf(), Receiver, SelPtr, &Args[0], Args.size()); diff --git a/lib/CodeGen/CGObjCEtoile.cpp b/lib/CodeGen/CGObjCEtoile.cpp index d5023b4240..94471ad4df 100644 --- a/lib/CodeGen/CGObjCEtoile.cpp +++ b/lib/CodeGen/CGObjCEtoile.cpp @@ -46,10 +46,15 @@ public: llvm::Value *getSelector(llvm::IRBuilder &Builder, llvm::Value *SelName, llvm::Value *SelTypes); - virtual llvm::Function *MethodPreamble(const llvm::Type *ReturnTy, + virtual llvm::Function *MethodPreamble( + const std::string &ClassName, + const std::string &CategoryName, + const std::string &MethodName, + const llvm::Type *ReturnTy, const llvm::Type *SelfTy, const llvm::Type **ArgTy, unsigned ArgC, + bool isClassMethod, bool isVarArg); }; } // end anonymous namespace @@ -207,10 +212,14 @@ llvm::Value *CGObjCEtoile::generateMessageSend(llvm::IRBuilder &Builder, /// Generates an LLVM Function object corresponding to the Objective-C method, /// including the implicit arguments. llvm::Function *CGObjCEtoile::MethodPreamble( + const std::string &ClassName, + const std::string &CategoryName, + const std::string &MethodName, const llvm::Type *ReturnTy, const llvm::Type *SelfTy, const llvm::Type **ArgTy, unsigned ArgC, + bool isClassMethod, bool isVarArg) { std::vector Args; //Args.push_back(SelfTy); diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index 8373cfd84f..ad38d9a8a4 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -1,4 +1,4 @@ -//===------- CGObjCGNU.cpp - Emit LLVM Code from ASTs for a Module --------===// +//===------- CGObjCGNU.cpp - Emit LLVM Code from ASTs for a Module --------===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,10 @@ // //===----------------------------------------------------------------------===// // -// This provides Objective-C code generation targetting the GNU runtime. +// This provides Objective-C code generation targetting the GNU runtime. The +// class in this file generates structures used by the GNU Objective-C runtime +// library. These structures are defined in objc/objc.h and objc/objc-api.h in +// the GNU runtime distribution. // //===----------------------------------------------------------------------===// @@ -16,11 +19,21 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/IRBuilder.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include + +using llvm::dyn_cast; + +// The version of the runtime that this class targets. Must match the version +// in the runtime. +static const int RuntimeVersion = 8; +static const int ProtocolVersion = 2; namespace { class CGObjCGNU : public clang::CodeGen::CGObjCRuntime { private: llvm::Module &TheModule; + const llvm::StructType *SelStructTy; const llvm::Type *SelectorTy; const llvm::Type *PtrToInt8Ty; const llvm::Type *IMPTy; @@ -29,28 +42,127 @@ private: const llvm::Type *PtrTy; const llvm::Type *LongTy; const llvm::Type *PtrToIntTy; + std::vector Classes; + std::vector Categories; + std::vector ConstantStrings; + llvm::Function *LoadFunction; + llvm::StringMap ExistingProtocols; + typedef std::pair TypedSelector; + std::map TypedSelectors; + llvm::StringMap UntypedSelectors; + // Some zeros used for GEPs in lots of places. + llvm::Constant *Zeros[2]; + llvm::Constant *NULLPtr; +private: + llvm::Constant *GenerateIvarList( + const llvm::SmallVectorImpl &IvarNames, + const llvm::SmallVectorImpl &IvarTypes, + const llvm::SmallVectorImpl &IvarOffsets); + llvm::Constant *GenerateMethodList(const std::string &ClassName, + const std::string &CategoryName, + const llvm::SmallVectorImpl &MethodNames, + const llvm::SmallVectorImpl &MethodTypes, + bool isClassMethodList); + llvm::Constant *GenerateProtocolList( + const llvm::SmallVectorImpl &Protocols); + llvm::Constant *GenerateClassStructure( + llvm::Constant *MetaClass, + llvm::Constant *SuperClass, + unsigned info, + llvm::Constant *Name, + llvm::Constant *Version, + llvm::Constant *InstanceSize, + llvm::Constant *IVars, + llvm::Constant *Methods, + llvm::Constant *Protocols); + llvm::Constant *GenerateProtocolMethodList( + const llvm::SmallVectorImpl &MethodNames, + const llvm::SmallVectorImpl &MethodTypes); + llvm::Constant *MakeConstantString(const std::string &Str, const std::string + &Name=""); + llvm::Constant *MakeGlobal(const llvm::StructType *Ty, + std::vector &V, const std::string &Name=""); + llvm::Constant *MakeGlobal(const llvm::ArrayType *Ty, + std::vector &V, const std::string &Name=""); public: CGObjCGNU(llvm::Module &Mp, const llvm::Type *LLVMIntType, const llvm::Type *LLVMLongType); - virtual llvm::Value *generateMessageSend(llvm::IRBuilder &Builder, + virtual llvm::Constant *GenerateConstantString(const char *String, + const size_t length); + virtual llvm::Value *GenerateMessageSend(llvm::IRBuilder &Builder, const llvm::Type *ReturnTy, llvm::Value *Sender, llvm::Value *Receiver, llvm::Value *Selector, llvm::Value** ArgV, unsigned ArgC); - llvm::Value *getSelector(llvm::IRBuilder &Builder, + virtual llvm::Value *GenerateMessageSendSuper(llvm::IRBuilder &Builder, + const llvm::Type *ReturnTy, + llvm::Value *Sender, + const char *SuperClassName, + llvm::Value *Receiver, + llvm::Value *Selector, + llvm::Value** ArgV, + unsigned ArgC); + virtual llvm::Value *LookupClass(llvm::IRBuilder &Builder, llvm::Value + *ClassName); + virtual llvm::Value *GetSelector(llvm::IRBuilder &Builder, llvm::Value *SelName, llvm::Value *SelTypes); - virtual llvm::Function *MethodPreamble(const llvm::Type *ReturnTy, - const llvm::Type *SelfTy, - const llvm::Type **ArgTy, - unsigned ArgC, - bool isVarArg); + virtual llvm::Function *MethodPreamble( + const std::string &ClassName, + const std::string &CategoryName, + const std::string &MethodName, + const llvm::Type *ReturnTy, + const llvm::Type *SelfTy, + const llvm::Type **ArgTy, + unsigned ArgC, + bool isClassMethod, + bool isVarArg); + virtual void GenerateCategory(const char *ClassName, const char *CategoryName, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes, + const llvm::SmallVectorImpl &Protocols); + virtual void GenerateClass( + const char *ClassName, + const char *SuperClassName, + const int instanceSize, + const llvm::SmallVectorImpl &IvarNames, + const llvm::SmallVectorImpl &IvarTypes, + const llvm::SmallVectorImpl &IvarOffsets, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes, + const llvm::SmallVectorImpl &Protocols); + virtual llvm::Value *GenerateProtocolRef(llvm::IRBuilder &Builder, const char + *ProtocolName); + virtual void GenerateProtocol(const char *ProtocolName, + const llvm::SmallVectorImpl &Protocols, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes); + virtual llvm::Function *ModuleInitFunction(); }; } // end anonymous namespace + + +static std::string SymbolNameForClass(const std::string &ClassName) { + return ".objc_class_" + ClassName; +} + +static std::string SymbolNameForMethod(const std::string &ClassName, const + std::string &CategoryName, const std::string &MethodName, bool isClassMethod) +{ + return "._objc_method_" + ClassName +"("+CategoryName+")"+ + (isClassMethod ? "+" : "-") + MethodName; +} + CGObjCGNU::CGObjCGNU(llvm::Module &M, const llvm::Type *LLVMIntType, const llvm::Type *LLVMLongType) : @@ -58,15 +170,20 @@ CGObjCGNU::CGObjCGNU(llvm::Module &M, IntTy(LLVMIntType), LongTy(LLVMLongType) { + Zeros[0] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0); + Zeros[1] = Zeros[0]; + NULLPtr = llvm::ConstantPointerNull::get( + llvm::PointerType::getUnqual(llvm::Type::Int8Ty)); // C string type. Used in lots of places. PtrToInt8Ty = llvm::PointerType::getUnqual(llvm::Type::Int8Ty); // Get the selector Type. - const llvm::Type *SelStructTy = llvm::StructType::get( + SelStructTy = llvm::StructType::get( PtrToInt8Ty, PtrToInt8Ty, NULL); SelectorTy = llvm::PointerType::getUnqual(SelStructTy); + M.addTypeName(".objc_selector", SelectorTy); PtrToIntTy = llvm::PointerType::getUnqual(IntTy); PtrTy = PtrToInt8Ty; @@ -77,24 +194,67 @@ CGObjCGNU::CGObjCGNU(llvm::Module &M, llvm::cast(OpaqueObjTy.get())->refineAbstractTypeTo(IdTy); IdTy = llvm::cast(OpaqueObjTy.get()); IdTy = llvm::PointerType::getUnqual(IdTy); + M.addTypeName(".objc_id", IdTy); // IMP type std::vector IMPArgs; IMPArgs.push_back(IdTy); IMPArgs.push_back(SelectorTy); IMPTy = llvm::FunctionType::get(IdTy, IMPArgs, true); - + M.addTypeName(".objc_imp", IMPTy); +} +// This has to perform the lookup every time, since posing and related +// techniques can modify the name -> class mapping. +llvm::Value *CGObjCGNU::LookupClass(llvm::IRBuilder &Builder, + llvm::Value *ClassName) { + llvm::Constant *ClassLookupFn = + TheModule.getOrInsertFunction("objc_lookup_class", IdTy, PtrToInt8Ty, + NULL); + return Builder.CreateCall(ClassLookupFn, ClassName); } /// Looks up the selector for the specified name / type pair. // FIXME: Selectors should be statically cached, not looked up on every call. -llvm::Value *CGObjCGNU::getSelector(llvm::IRBuilder &Builder, +llvm::Value *CGObjCGNU::GetSelector(llvm::IRBuilder &Builder, llvm::Value *SelName, - llvm::Value *SelTypes) -{ - // Look up the selector. + llvm::Value *SelTypes) { + // For static selectors, we return an alias for now then store them all in a + // list that the runtime will initialise later. + if (llvm::Constant *CName = dyn_cast(SelName)) { + // Untyped selector + if (SelTypes == 0) { + // If it's already cached, return it. + if (UntypedSelectors[CName->getStringValue()]) { + return Builder.CreateLoad(UntypedSelectors[CName->getStringValue()]); + } + // If it isn't, cache it. + llvm::GlobalAlias *Sel = new llvm::GlobalAlias( + llvm::PointerType::getUnqual(SelectorTy), + llvm::GlobalValue::InternalLinkage, ".objc_untyped_selector_alias", + NULL, &TheModule); + UntypedSelectors[CName->getStringValue()] = Sel; + return Builder.CreateLoad(Sel); + } + // Typed selectors + if (llvm::Constant *CTypes = dyn_cast(SelTypes)) { + TypedSelector Selector = TypedSelector(CName->getStringValue(), + CTypes->getStringValue()); + // If it's already cached, return it. + if (TypedSelectors[Selector]) { + return Builder.CreateLoad(TypedSelectors[Selector]); + } + // If it isn't, cache it. + llvm::GlobalAlias *Sel = new llvm::GlobalAlias( + llvm::PointerType::getUnqual(SelectorTy), + llvm::GlobalValue::InternalLinkage, ".objc_typed_selector_alias", + NULL, &TheModule); + TypedSelectors[Selector] = Sel; + return Builder.CreateLoad(Sel); + } + } + // Dynamically look up selectors from non-constant sources llvm::Value *cmd; - if(SelTypes == 0) { + if (SelTypes == 0) { llvm::Constant *SelFunction = TheModule.getOrInsertFunction("sel_get_uid", SelectorTy, PtrToInt8Ty, @@ -114,27 +274,121 @@ llvm::Value *CGObjCGNU::getSelector(llvm::IRBuilder &Builder, } -/// Generate code for a message send expression on the GNU runtime. -// FIXME: Much of this code will need factoring out later. -// TODO: This should take a sender argument (pointer to self in the calling -// context) -llvm::Value *CGObjCGNU::generateMessageSend(llvm::IRBuilder &Builder, +llvm::Constant *CGObjCGNU::MakeConstantString(const std::string &Str, const + std::string &Name) { + llvm::Constant * ConstStr = llvm::ConstantArray::get(Str); + ConstStr = new llvm::GlobalVariable(ConstStr->getType(), true, + llvm::GlobalValue::InternalLinkage, + ConstStr, Name, &TheModule); + return llvm::ConstantExpr::getGetElementPtr(ConstStr, Zeros, 2); +} +llvm::Constant *CGObjCGNU::MakeGlobal(const llvm::StructType *Ty, + std::vector &V, const std::string &Name) { + llvm::Constant *C = llvm::ConstantStruct::get(Ty, V); + return new llvm::GlobalVariable(Ty, false, + llvm::GlobalValue::InternalLinkage, C, Name, &TheModule); +} +llvm::Constant *CGObjCGNU::MakeGlobal(const llvm::ArrayType *Ty, + std::vector &V, const std::string &Name) { + llvm::Constant *C = llvm::ConstantArray::get(Ty, V); + return new llvm::GlobalVariable(Ty, false, + llvm::GlobalValue::InternalLinkage, C, Name, &TheModule); +} + +/// Generate an NSConstantString object. +//TODO: In case there are any crazy people still using the GNU runtime without +//an OpenStep implementation, this should let them select their own class for +//constant strings. +llvm::Constant *CGObjCGNU::GenerateConstantString(const char *String, const + size_t length) { + std::vector Ivars; + Ivars.push_back(NULLPtr); + Ivars.push_back(MakeConstantString(String)); + Ivars.push_back(llvm::ConstantInt::get(IntTy, length)); + llvm::Constant *ObjCStr = MakeGlobal( + llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, IntTy, NULL), + Ivars, ".objc_str"); + ConstantStrings.push_back( + llvm::ConstantExpr::getBitCast(ObjCStr, PtrToInt8Ty)); + return ObjCStr; +} + +///Generates a message send where the super is the receiver. This is a message +///send to self with special delivery semantics indicating which class's method +///should be called. +llvm::Value *CGObjCGNU::GenerateMessageSendSuper(llvm::IRBuilder &Builder, + const llvm::Type *ReturnTy, + llvm::Value *Sender, + const char *SuperClassName, + llvm::Value *Receiver, + llvm::Value *Selector, + llvm::Value** ArgV, + unsigned ArgC) { + // TODO: This should be cached, not looked up every time. + llvm::Value *ReceiverClass = LookupClass(Builder, + MakeConstantString(SuperClassName)); + llvm::Value *cmd = GetSelector(Builder, Selector, 0); + std::vector impArgTypes; + impArgTypes.push_back(Receiver->getType()); + impArgTypes.push_back(SelectorTy); + + // Avoid an explicit cast on the IMP by getting a version that has the right + // return type. + llvm::FunctionType *impType = llvm::FunctionType::get(ReturnTy, impArgTypes, + true); + // Construct the structure used to look up the IMP + llvm::StructType *ObjCSuperTy = llvm::StructType::get(Receiver->getType(), + IdTy, NULL); + llvm::Value *ObjCSuper = Builder.CreateAlloca(ObjCSuperTy); + Builder.CreateStore(Receiver, Builder.CreateStructGEP(ObjCSuper, 0)); + Builder.CreateStore(ReceiverClass, Builder.CreateStructGEP(ObjCSuper, 1)); + + // Get the IMP + llvm::Constant *lookupFunction = + TheModule.getOrInsertFunction("objc_msg_lookup_super", + llvm::PointerType::getUnqual(impType), + llvm::PointerType::getUnqual(ObjCSuperTy), + SelectorTy, NULL); + llvm::Value *lookupArgs[] = {ObjCSuper, cmd}; + llvm::Value *imp = Builder.CreateCall(lookupFunction, lookupArgs, + lookupArgs+2); + + // Call the method + llvm::SmallVector callArgs; + callArgs.push_back(Receiver); + callArgs.push_back(cmd); + callArgs.insert(callArgs.end(), ArgV, ArgV+ArgC); + return Builder.CreateCall(imp, callArgs.begin(), callArgs.end()); +} + +/// Generate code for a message send expression. +llvm::Value *CGObjCGNU::GenerateMessageSend(llvm::IRBuilder &Builder, const llvm::Type *ReturnTy, llvm::Value *Sender, llvm::Value *Receiver, llvm::Value *Selector, llvm::Value** ArgV, unsigned ArgC) { - llvm::Value *cmd = getSelector(Builder, Selector, 0); + llvm::Value *cmd = GetSelector(Builder, Selector, 0); // Look up the method implementation. std::vector impArgTypes; + const llvm::Type *RetTy; + //TODO: Revisit this when LLVM supports aggregate return types. + if (ReturnTy->isSingleValueType() && ReturnTy != llvm::Type::VoidTy) { + RetTy = ReturnTy; + } else { + // For struct returns allocate the space in the caller and pass it up to + // the sender. + RetTy = llvm::Type::VoidTy; + impArgTypes.push_back(llvm::PointerType::getUnqual(ReturnTy)); + } impArgTypes.push_back(Receiver->getType()); impArgTypes.push_back(SelectorTy); // Avoid an explicit cast on the IMP by getting a version that has the right // return type. - llvm::FunctionType *impType = llvm::FunctionType::get(ReturnTy, impArgTypes, + llvm::FunctionType *impType = llvm::FunctionType::get(RetTy, impArgTypes, true); llvm::Constant *lookupFunction = @@ -144,20 +398,489 @@ llvm::Value *CGObjCGNU::generateMessageSend(llvm::IRBuilder &Builder, llvm::Value *imp = Builder.CreateCall2(lookupFunction, Receiver, cmd); // Call the method. - llvm::SmallVector lookupArgs; - lookupArgs.push_back(Receiver); - lookupArgs.push_back(cmd); - lookupArgs.insert(lookupArgs.end(), ArgV, ArgV+ArgC); - return Builder.CreateCall(imp, lookupArgs.begin(), lookupArgs.end()); + llvm::SmallVector Args; + if (!ReturnTy->isSingleValueType()) { + llvm::Value *Return = Builder.CreateAlloca(ReturnTy); + Args.push_back(Return); + return Return; + } + Args.push_back(Receiver); + Args.push_back(cmd); + Args.insert(Args.end(), ArgV, ArgV+ArgC); + return Builder.CreateCall(imp, Args.begin(), Args.end()); +} + +/// Generates a MethodList. Used in construction of a objc_class and +/// objc_category structures. +llvm::Constant *CGObjCGNU::GenerateMethodList(const std::string &ClassName, + const std::string &CategoryName, + const llvm::SmallVectorImpl &MethodNames, + const llvm::SmallVectorImpl &MethodTypes, + bool isClassMethodList) { + // Get the method structure type. + llvm::StructType *ObjCMethodTy = llvm::StructType::get( + PtrToInt8Ty, // Really a selector, but the runtime creates it us. + PtrToInt8Ty, // Method types + llvm::PointerType::getUnqual(IMPTy), //Method pointer + NULL); + std::vector Methods; + std::vector Elements; + for (unsigned int i = 0, e = MethodTypes.size(); i < e; ++i) { + Elements.clear(); + Elements.push_back( llvm::ConstantExpr::getGetElementPtr(MethodNames[i], + Zeros, 2)); + Elements.push_back( + llvm::ConstantExpr::getGetElementPtr(MethodTypes[i], Zeros, 2)); + llvm::Constant *Method = + TheModule.getFunction(SymbolNameForMethod(ClassName, CategoryName, + MethodNames[i]->getStringValue(), isClassMethodList)); + Method = llvm::ConstantExpr::getBitCast(Method, + llvm::PointerType::getUnqual(IMPTy)); + Elements.push_back(Method); + Methods.push_back(llvm::ConstantStruct::get(ObjCMethodTy, Elements)); + } + + // Array of method structures + llvm::ArrayType *ObjCMethodArrayTy = llvm::ArrayType::get(ObjCMethodTy, + MethodNames.size()); + llvm::Constant *MethodArray = llvm::ConstantArray::get(ObjCMethodArrayTy, + Methods); + + // Structure containing list pointer, array and array count + llvm::SmallVector ObjCMethodListFields; + llvm::PATypeHolder OpaqueNextTy = llvm::OpaqueType::get(); + llvm::Type *NextPtrTy = llvm::PointerType::getUnqual(OpaqueNextTy); + llvm::StructType *ObjCMethodListTy = llvm::StructType::get(NextPtrTy, + IntTy, + ObjCMethodArrayTy, + NULL); + // Refine next pointer type to concrete type + llvm::cast( + OpaqueNextTy.get())->refineAbstractTypeTo(ObjCMethodListTy); + ObjCMethodListTy = llvm::cast(OpaqueNextTy.get()); + + Methods.clear(); + Methods.push_back(llvm::ConstantPointerNull::get( + llvm::PointerType::getUnqual(ObjCMethodListTy))); + Methods.push_back(llvm::ConstantInt::get(llvm::Type::Int32Ty, + MethodTypes.size())); + Methods.push_back(MethodArray); + + // Create an instance of the structure + return MakeGlobal(ObjCMethodListTy, Methods, ".objc_method_list"); +} + +/// Generates an IvarList. Used in construction of a objc_class. +llvm::Constant *CGObjCGNU::GenerateIvarList( + const llvm::SmallVectorImpl &IvarNames, + const llvm::SmallVectorImpl &IvarTypes, + const llvm::SmallVectorImpl &IvarOffsets) { + // Get the method structure type. + llvm::StructType *ObjCIvarTy = llvm::StructType::get( + PtrToInt8Ty, + PtrToInt8Ty, + IntTy, + NULL); + std::vector Ivars; + std::vector Elements; + for (unsigned int i = 0, e = IvarNames.size() ; i < e ; i++) { + Elements.clear(); + Elements.push_back( llvm::ConstantExpr::getGetElementPtr(IvarNames[i], + Zeros, 2)); + Elements.push_back( llvm::ConstantExpr::getGetElementPtr(IvarTypes[i], + Zeros, 2)); + Elements.push_back(IvarOffsets[i]); + Ivars.push_back(llvm::ConstantStruct::get(ObjCIvarTy, Elements)); + } + + // Array of method structures + llvm::ArrayType *ObjCIvarArrayTy = llvm::ArrayType::get(ObjCIvarTy, + IvarNames.size()); + + + Elements.clear(); + Elements.push_back(llvm::ConstantInt::get( + llvm::cast(IntTy), (int)IvarNames.size())); + Elements.push_back(llvm::ConstantArray::get(ObjCIvarArrayTy, Ivars)); + // Structure containing array and array count + llvm::StructType *ObjCIvarListTy = llvm::StructType::get(IntTy, + ObjCIvarArrayTy, + NULL); + + // Create an instance of the structure + return MakeGlobal(ObjCIvarListTy, Elements, ".objc_ivar_list"); } +/// Generate a class structure +llvm::Constant *CGObjCGNU::GenerateClassStructure( + llvm::Constant *MetaClass, + llvm::Constant *SuperClass, + unsigned info, + llvm::Constant *Name, + llvm::Constant *Version, + llvm::Constant *InstanceSize, + llvm::Constant *IVars, + llvm::Constant *Methods, + llvm::Constant *Protocols) { + // Set up the class structure + // Note: Several of these are char*s when they should be ids. This is + // because the runtime performs this translation on load. + llvm::StructType *ClassTy = llvm::StructType::get( + PtrToInt8Ty, // class_pointer + PtrToInt8Ty, // super_class + PtrToInt8Ty, // name + LongTy, // version + LongTy, // info + LongTy, // instance_size + IVars->getType(), // ivars + Methods->getType(), // methods + // These are all filled in by the runtime, so we pretend + PtrTy, // dtable + PtrTy, // subclass_list + PtrTy, // sibling_class + PtrTy, // protocols + PtrTy, // gc_object_type + NULL); + llvm::Constant *Zero = llvm::ConstantInt::get(LongTy, 0); + llvm::Constant *NullP = + llvm::ConstantPointerNull::get(llvm::cast(PtrTy)); + // Fill in the structure + std::vector Elements; + Elements.push_back(llvm::ConstantExpr::getBitCast(MetaClass, PtrToInt8Ty)); + Elements.push_back(SuperClass); + Elements.push_back(Name); + Elements.push_back(Zero); + Elements.push_back(llvm::ConstantInt::get(LongTy, info)); + Elements.push_back(InstanceSize); + Elements.push_back(IVars); + Elements.push_back(Methods); + Elements.push_back(NullP); + Elements.push_back(NullP); + Elements.push_back(NullP); + Elements.push_back(llvm::ConstantExpr::getBitCast(Protocols, PtrTy)); + Elements.push_back(NullP); + // Create an instance of the structure + return MakeGlobal(ClassTy, Elements, + SymbolNameForClass(Name->getStringValue())); +} + +llvm::Constant *CGObjCGNU::GenerateProtocolMethodList( + const llvm::SmallVectorImpl &MethodNames, + const llvm::SmallVectorImpl &MethodTypes) { + // Get the method structure type. + llvm::StructType *ObjCMethodDescTy = llvm::StructType::get( + PtrToInt8Ty, // Really a selector, but the runtime does the casting for us. + PtrToInt8Ty, + NULL); + std::vector Methods; + std::vector Elements; + for (unsigned int i = 0, e = MethodTypes.size() ; i < e ; i++) { + Elements.clear(); + Elements.push_back( llvm::ConstantExpr::getGetElementPtr(MethodNames[i], + Zeros, 2)); + Elements.push_back( + llvm::ConstantExpr::getGetElementPtr(MethodTypes[i], Zeros, 2)); + Methods.push_back(llvm::ConstantStruct::get(ObjCMethodDescTy, Elements)); + } + llvm::ArrayType *ObjCMethodArrayTy = llvm::ArrayType::get(ObjCMethodDescTy, + MethodNames.size()); + llvm::Constant *Array = llvm::ConstantArray::get(ObjCMethodArrayTy, Methods); + llvm::StructType *ObjCMethodDescListTy = llvm::StructType::get( + IntTy, ObjCMethodArrayTy, NULL); + Methods.clear(); + Methods.push_back(llvm::ConstantInt::get(IntTy, MethodNames.size())); + Methods.push_back(Array); + return MakeGlobal(ObjCMethodDescListTy, Methods, ".objc_method_list"); +} +// Create the protocol list structure used in classes, categories and so on +llvm::Constant *CGObjCGNU::GenerateProtocolList( + const llvm::SmallVectorImpl &Protocols) { + llvm::ArrayType *ProtocolArrayTy = llvm::ArrayType::get(PtrToInt8Ty, + Protocols.size()); + llvm::StructType *ProtocolListTy = llvm::StructType::get( + PtrTy, //Should be a recurisve pointer, but it's always NULL here. + LongTy,//FIXME: Should be size_t + ProtocolArrayTy, + NULL); + std::vector Elements; + for (const std::string *iter = Protocols.begin(), *endIter = Protocols.end(); + iter != endIter ; iter++) { + llvm::Constant *Ptr = + llvm::ConstantExpr::getBitCast(ExistingProtocols[*iter], PtrToInt8Ty); + Elements.push_back(Ptr); + } + llvm::Constant * ProtocolArray = llvm::ConstantArray::get(ProtocolArrayTy, + Elements); + Elements.clear(); + Elements.push_back(NULLPtr); + Elements.push_back(llvm::ConstantInt::get( + llvm::cast(LongTy), Protocols.size())); + Elements.push_back(ProtocolArray); + return MakeGlobal(ProtocolListTy, Elements, ".objc_protocol_list"); +} + +llvm::Value *CGObjCGNU::GenerateProtocolRef(llvm::IRBuilder &Builder, const + char *ProtocolName) { + return ExistingProtocols[ProtocolName]; +} + +void CGObjCGNU::GenerateProtocol(const char *ProtocolName, + const llvm::SmallVectorImpl &Protocols, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes) { + + llvm::Constant *ProtocolList = GenerateProtocolList(Protocols); + llvm::Constant *InstanceMethodList = + GenerateProtocolMethodList(InstanceMethodNames, InstanceMethodTypes); + llvm::Constant *ClassMethodList = + GenerateProtocolMethodList(ClassMethodNames, ClassMethodTypes); + // Protocols are objects containing lists of the methods implemented and + // protocols adopted. + llvm::StructType *ProtocolTy = llvm::StructType::get(IdTy, + PtrToInt8Ty, + ProtocolList->getType(), + InstanceMethodList->getType(), + ClassMethodList->getType(), + NULL); + std::vector Elements; + // The isa pointer must be set to a magic number so the runtime knows it's + // the correct layout. + Elements.push_back(llvm::ConstantExpr::getIntToPtr( + llvm::ConstantInt::get(llvm::Type::Int32Ty, ProtocolVersion), IdTy)); + Elements.push_back(MakeConstantString(ProtocolName, ".objc_protocol_name")); + Elements.push_back(ProtocolList); + Elements.push_back(InstanceMethodList); + Elements.push_back(ClassMethodList); + ExistingProtocols[ProtocolName] = + llvm::ConstantExpr::getBitCast(MakeGlobal(ProtocolTy, Elements, + ".objc_protocol"), IdTy); +} + +void CGObjCGNU::GenerateCategory( + const char *ClassName, + const char *CategoryName, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes, + const llvm::SmallVectorImpl &Protocols) { + std::vector Elements; + Elements.push_back(MakeConstantString(CategoryName)); + Elements.push_back(MakeConstantString(ClassName)); + // Instance method list + Elements.push_back(llvm::ConstantExpr::getBitCast(GenerateMethodList( + ClassName, CategoryName, InstanceMethodNames, InstanceMethodTypes, + false), PtrTy)); + // Class method list + Elements.push_back(llvm::ConstantExpr::getBitCast(GenerateMethodList( + ClassName, CategoryName, ClassMethodNames, ClassMethodTypes, true), + PtrTy)); + // Protocol list + Elements.push_back(llvm::ConstantExpr::getBitCast( + GenerateProtocolList(Protocols), PtrTy)); + Categories.push_back(llvm::ConstantExpr::getBitCast( + MakeGlobal(llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, PtrTy, + PtrTy, PtrTy, NULL), Elements), PtrTy)); +} +void CGObjCGNU::GenerateClass( + const char *ClassName, + const char *SuperClassName, + const int instanceSize, + const llvm::SmallVectorImpl &IvarNames, + const llvm::SmallVectorImpl &IvarTypes, + const llvm::SmallVectorImpl &IvarOffsets, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes, + const llvm::SmallVectorImpl &Protocols) { + // Get the superclass pointer. + llvm::Constant *SuperClass; + if (SuperClassName) { + SuperClass = MakeConstantString(SuperClassName, ".super_class_name"); + } else { + SuperClass = llvm::ConstantPointerNull::get( + llvm::cast(PtrToInt8Ty)); + } + llvm::Constant * Name = MakeConstantString(ClassName, ".class_name"); + // Empty vector used to construct empty method lists + llvm::SmallVector empty; + // Generate the method and instance variable lists + llvm::Constant *MethodList = GenerateMethodList(ClassName, "", + InstanceMethodNames, InstanceMethodTypes, false); + llvm::Constant *ClassMethodList = GenerateMethodList(ClassName, "", + ClassMethodNames, ClassMethodTypes, true); + llvm::Constant *IvarList = GenerateIvarList(IvarNames, IvarTypes, + IvarOffsets); + //Generate metaclass for class methods + llvm::Constant *MetaClassStruct = GenerateClassStructure(NULLPtr, + NULLPtr, 0x2L, NULLPtr, 0, Zeros[0], GenerateIvarList( + empty, empty, empty), ClassMethodList, NULLPtr); + // Generate the class structure + llvm::Constant *ClassStruct = GenerateClassStructure(MetaClassStruct, + SuperClass, 0x1L, Name, 0, + llvm::ConstantInt::get(llvm::Type::Int32Ty, instanceSize), IvarList, + MethodList, GenerateProtocolList(Protocols)); + // Add class structure to list to be added to the symtab later + ClassStruct = llvm::ConstantExpr::getBitCast(ClassStruct, PtrToInt8Ty); + Classes.push_back(ClassStruct); +} + +llvm::Function *CGObjCGNU::ModuleInitFunction() { + // Only emit an ObjC load function if no Objective-C stuff has been called + if (Classes.empty() && Categories.empty() && ConstantStrings.empty() && + ExistingProtocols.empty() && TypedSelectors.empty() && + UntypedSelectors.empty() == 0) + return NULL; + + std::vector Elements; + // Generate statics list: + llvm::ArrayType *StaticsArrayTy = llvm::ArrayType::get(PtrToInt8Ty, + ConstantStrings.size() + 1); + ConstantStrings.push_back(NULLPtr); + Elements.push_back(MakeConstantString("NSConstantString", + ".objc_static_class_name")); + Elements.push_back(llvm::ConstantArray::get(StaticsArrayTy, ConstantStrings)); + llvm::StructType *StaticsListTy = + llvm::StructType::get(PtrToInt8Ty, StaticsArrayTy, NULL); + llvm::Constant *Statics = + MakeGlobal(StaticsListTy, Elements, ".objc_statics"); + Statics = new + llvm::GlobalVariable(llvm::PointerType::getUnqual(StaticsListTy), false, + llvm::GlobalValue::InternalLinkage, Statics, ".objc_statics_ptr", + &TheModule); + Statics = llvm::ConstantExpr::getBitCast(Statics, PtrTy); + // Array of classes, categories, and constant objects + llvm::ArrayType *ClassListTy = llvm::ArrayType::get(PtrToInt8Ty, + Classes.size() + Categories.size() + 2); + llvm::StructType *SymTabTy = llvm::StructType::get( + LongTy, + SelectorTy, + llvm::Type::Int16Ty, + llvm::Type::Int16Ty, + ClassListTy, + NULL); + + Elements.clear(); + // Pointer to an array of selectors used in this module. + std::vector Selectors; + for (std::map::iterator + iter = TypedSelectors.begin(), iterEnd = TypedSelectors.end(); + iter != iterEnd ; ++iter) { + Elements.push_back(MakeConstantString((*iter).first.first, + ".objc_sel_name")); + Elements.push_back(MakeConstantString((*iter).first.second, + ".objc_sel_types")); + Selectors.push_back(llvm::ConstantStruct::get(SelStructTy, Elements)); + Elements.clear(); + } + for (llvm::StringMap::iterator + iter = UntypedSelectors.begin(), iterEnd = UntypedSelectors.end(); + iter != iterEnd; iter++) { + Elements.push_back( + MakeConstantString((*iter).getKeyData(), ".objc_sel_name")); + Elements.push_back(NULLPtr); + Selectors.push_back(llvm::ConstantStruct::get(SelStructTy, Elements)); + Elements.clear(); + } + Elements.push_back(NULLPtr); + Elements.push_back(NULLPtr); + Selectors.push_back(llvm::ConstantStruct::get(SelStructTy, Elements)); + Elements.clear(); + // Number of static selectors + Elements.push_back(llvm::ConstantInt::get(LongTy, Selectors.size() )); + llvm::Constant *SelectorList = MakeGlobal( + llvm::ArrayType::get(SelStructTy, Selectors.size()), Selectors, + ".objc_selector_list"); + Elements.push_back(llvm::ConstantExpr::getBitCast(SelectorList, SelectorTy)); + + // Now that all of the static selectors exist, create pointers to them. + int index = 0; + for (std::map::iterator + iter=TypedSelectors.begin(), iterEnd =TypedSelectors.end(); + iter != iterEnd; ++iter) { + llvm::Constant *Idxs[] = {Zeros[0], + llvm::ConstantInt::get(llvm::Type::Int32Ty, index++), Zeros[0]}; + llvm::GlobalVariable *SelPtr = new llvm::GlobalVariable(SelectorTy, true, + llvm::GlobalValue::InternalLinkage, + llvm::ConstantExpr::getGetElementPtr(SelectorList, Idxs, 2), + ".objc_sel_ptr", &TheModule); + (*iter).second->setAliasee(SelPtr); + } + for (llvm::StringMap::iterator + iter=UntypedSelectors.begin(), iterEnd = UntypedSelectors.end(); + iter != iterEnd; iter++) { + llvm::Constant *Idxs[] = {Zeros[0], + llvm::ConstantInt::get(llvm::Type::Int32Ty, index++), Zeros[0]}; + llvm::GlobalVariable *SelPtr = new llvm::GlobalVariable(SelectorTy, true, + llvm::GlobalValue::InternalLinkage, + llvm::ConstantExpr::getGetElementPtr(SelectorList, Idxs, 2), + ".objc_sel_ptr", &TheModule); + (*iter).second->setAliasee(SelPtr); + } + // Number of classes defined. + Elements.push_back(llvm::ConstantInt::get(llvm::Type::Int16Ty, + Classes.size())); + // Number of categories defined + Elements.push_back(llvm::ConstantInt::get(llvm::Type::Int16Ty, + Categories.size())); + // Create an array of classes, then categories, then static object instances + Classes.insert(Classes.end(), Categories.begin(), Categories.end()); + // NULL-terminated list of static object instances (mainly constant strings) + Classes.push_back(Statics); + Classes.push_back(NULLPtr); + llvm::Constant *ClassList = llvm::ConstantArray::get(ClassListTy, Classes); + Elements.push_back(ClassList); + // Construct the symbol table + llvm::Constant *SymTab= MakeGlobal(SymTabTy, Elements); + + // The symbol table is contained in a module which has some version-checking + // constants + llvm::StructType * ModuleTy = llvm::StructType::get(LongTy, LongTy, + PtrToInt8Ty, llvm::PointerType::getUnqual(SymTabTy), NULL); + Elements.clear(); + // Runtime version used for compatibility checking. + Elements.push_back(llvm::ConstantInt::get(LongTy, RuntimeVersion)); + //FIXME: Should be sizeof(ModuleTy) + Elements.push_back(llvm::ConstantInt::get(LongTy, 16)); + //FIXME: Should be the path to the file where this module was declared + Elements.push_back(NULLPtr); + Elements.push_back(SymTab); + llvm::Value *Module = MakeGlobal(ModuleTy, Elements); + + // Create the load function calling the runtime entry point with the module + // structure + std::vector VoidArgs; + llvm::Function * LoadFunction = llvm::Function::Create( + llvm::FunctionType::get(llvm::Type::VoidTy, VoidArgs, false), + llvm::GlobalValue::InternalLinkage, ".objc_load_function", + &TheModule); + llvm::BasicBlock *EntryBB = llvm::BasicBlock::Create("entry", LoadFunction); + llvm::IRBuilder Builder; + Builder.SetInsertPoint(EntryBB); + llvm::Value *Register = TheModule.getOrInsertFunction("__objc_exec_class", + llvm::Type::VoidTy, llvm::PointerType::getUnqual(ModuleTy), NULL); + Builder.CreateCall(Register, Module); + Builder.CreateRetVoid(); + return LoadFunction; +} llvm::Function *CGObjCGNU::MethodPreamble( + const std::string &ClassName, + const std::string &CategoryName, + const std::string &MethodName, const llvm::Type *ReturnTy, const llvm::Type *SelfTy, const llvm::Type **ArgTy, unsigned ArgC, + bool isClassMethod, bool isVarArg) { std::vector Args; + if (!ReturnTy->isFirstClassType() && ReturnTy != llvm::Type::VoidTy) { + Args.push_back(llvm::PointerType::getUnqual(ReturnTy)); + ReturnTy = llvm::Type::VoidTy; + } Args.push_back(SelfTy); Args.push_back(SelectorTy); Args.insert(Args.end(), ArgTy, ArgTy+ArgC); @@ -165,12 +888,20 @@ llvm::Function *CGObjCGNU::MethodPreamble( llvm::FunctionType *MethodTy = llvm::FunctionType::get(ReturnTy, Args, isVarArg); + std::string FunctionName = SymbolNameForMethod(ClassName, CategoryName, + MethodName, isClassMethod); + llvm::Function *Method = llvm::Function::Create(MethodTy, llvm::GlobalValue::InternalLinkage, - ".objc.method", + FunctionName, &TheModule); - // Set the names of the hidden arguments llvm::Function::arg_iterator AI = Method->arg_begin(); + // Name the struct return argument. + // FIXME: This is probably the wrong test. + if (!ReturnTy->isFirstClassType() && ReturnTy != llvm::Type::VoidTy) { + AI->setName("agg.result"); + ++AI; + } AI->setName("self"); ++AI; AI->setName("_cmd"); diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index 9ee067bb21..6ec6e0ceea 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/lib/CodeGen/CGObjCRuntime.h @@ -15,6 +15,7 @@ #ifndef CLANG_CODEGEN_OBCJRUNTIME_H #define CLANG_CODEGEN_OBCJRUNTIME_H +#include "llvm/ADT/SmallVector.h" namespace llvm { class IRBuilder; @@ -25,32 +26,89 @@ namespace llvm { class Function; } - namespace clang { namespace CodeGen { -// Implements runtime-specific code generation functions +//FIXME Several methods should be pure virtual but aren't to avoid the +//partially-implemented subclass breaking. + +/// Implements runtime-specific code generation functions. class CGObjCRuntime { public: virtual ~CGObjCRuntime(); /// Generate an Objective-C message send operation - virtual llvm::Value *generateMessageSend(llvm::IRBuilder &Builder, + virtual llvm::Value *GenerateMessageSend(llvm::IRBuilder &Builder, const llvm::Type *ReturnTy, llvm::Value *Sender, llvm::Value *Receiver, llvm::Value *Selector, llvm::Value** ArgV, - unsigned ArgC) = 0; + unsigned ArgC) =0; /// Generate the function required to register all Objective-C components in /// this compilation unit with the runtime library. - virtual llvm::Function *ModuleInitFunction() { return 0; } + virtual llvm::Function *ModuleInitFunction() =0; + /// Get a selector for the specified name and type values + virtual llvm::Value *GetSelector(llvm::IRBuilder &Builder, + llvm::Value *SelName, + llvm::Value *SelTypes) =0; + /// Generate a constant string object + virtual llvm::Constant *GenerateConstantString(const char *String, const size_t + length) =0; + /// Generate a category. A category contains a list of methods (and + /// accompanying metadata) and a list of protocols. + virtual void GenerateCategory(const char *ClassName, const char *CategoryName, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes, + const llvm::SmallVectorImpl &Protocols) =0; + /// Generate a class stucture for this class. + virtual void GenerateClass( + const char *ClassName, + const char *SuperClassName, + const int instanceSize, + const llvm::SmallVectorImpl &IvarNames, + const llvm::SmallVectorImpl &IvarTypes, + const llvm::SmallVectorImpl &IvarOffsets, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes, + const llvm::SmallVectorImpl &Protocols) =0; + /// Generate a reference to the named protocol. + virtual llvm::Value *GenerateProtocolRef(llvm::IRBuilder &Builder, const char + *ProtocolName) =0; + virtual llvm::Value *GenerateMessageSendSuper(llvm::IRBuilder &Builder, + const llvm::Type *ReturnTy, + llvm::Value *Sender, + const char *SuperClassName, + llvm::Value *Receiver, + llvm::Value *Selector, + llvm::Value** ArgV, + unsigned ArgC) {return NULL;}; + /// Generate the named protocol. Protocols contain method metadata but no + /// implementations. + virtual void GenerateProtocol(const char *ProtocolName, + const llvm::SmallVectorImpl &Protocols, + const llvm::SmallVectorImpl &InstanceMethodNames, + const llvm::SmallVectorImpl &InstanceMethodTypes, + const llvm::SmallVectorImpl &ClassMethodNames, + const llvm::SmallVectorImpl &ClassMethodTypes) =0; /// Generate a function preamble for a method with the specified types - virtual llvm::Function *MethodPreamble(const llvm::Type *ReturnTy, + virtual llvm::Function *MethodPreamble( + const std::string &ClassName, + const std::string &CategoryName, + const std::string &MethodName, + const llvm::Type *ReturnTy, const llvm::Type *SelfTy, const llvm::Type **ArgTy, unsigned ArgC, + bool isClassMethod, bool isVarArg) = 0; + /// Look up the class for the specified name + virtual llvm::Value *LookupClass(llvm::IRBuilder &Builder, llvm::Value + *ClassName) =0; /// If instance variable addresses are determined at runtime then this should /// return true, otherwise instance variables will be accessed directly from /// the structure. If this returns true then @defs is invalid for this diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 0a016c6416..26a0cfbafb 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -65,11 +65,22 @@ void CodeGenFunction::GenerateObjCMethod(const ObjCMethodDecl *OMD) { for (unsigned i=0 ; iparam_size() ; i++) { ParamTypes.push_back(ConvertType(OMD->getParamDecl(i)->getType())); } - CurFn =CGM.getObjCRuntime()->MethodPreamble(ConvertType(OMD->getResultType()), - llvm::PointerType::getUnqual(llvm::Type::Int32Ty), - ParamTypes.begin(), - OMD->param_size(), - OMD->isVariadic()); + std::string CategoryName = ""; + if (ObjCCategoryImplDecl *OCD = + dyn_cast(OMD->getMethodContext())) { + CategoryName = OCD->getName(); + } + + CurFn =CGM.getObjCRuntime()->MethodPreamble( + OMD->getClassInterface()->getName(), + CategoryName, + OMD->getSelector().getName(), + ConvertType(OMD->getResultType()), + llvm::PointerType::getUnqual(llvm::Type::Int32Ty), + ParamTypes.begin(), + OMD->param_size(), + !OMD->isInstance(), + OMD->isVariadic()); llvm::BasicBlock *EntryBB = llvm::BasicBlock::Create("entry", CurFn); // Create a marker to make it easy to insert allocas into the entryblock diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 633aa209d4..6b9e69e13d 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -25,6 +25,7 @@ #include "llvm/DerivedTypes.h" #include "llvm/Module.h" #include "llvm/Intrinsics.h" +#include "llvm/Target/TargetData.h" #include "llvm/Analysis/Verifier.h" #include using namespace clang; @@ -50,10 +51,10 @@ CodeGenModule::CodeGenModule(ASTContext &C, const LangOptions &LO, } CodeGenModule::~CodeGenModule() { + EmitStatics(); llvm::Function *ObjCInitFunction = Runtime->ModuleInitFunction(); if (ObjCInitFunction) AddGlobalCtor(ObjCInitFunction); - EmitStatics(); EmitGlobalCtors(); EmitAnnotations(); delete Runtime; @@ -279,6 +280,154 @@ void CodeGenModule::EmitObjCMethod(const ObjCMethodDecl *OMD) { if (OMD->getBody()) CodeGenFunction(*this).GenerateObjCMethod(OMD); } +void CodeGenModule::EmitObjCProtocolImplementation(const ObjCProtocolDecl *PD){ + llvm::SmallVector Protocols; + for (unsigned i = 0, e = PD->getNumReferencedProtocols() ; i < e ; i++) + Protocols.push_back(PD->getReferencedProtocols()[i]->getName()); + llvm::SmallVector InstanceMethodNames; + llvm::SmallVector InstanceMethodTypes; + for (ObjCProtocolDecl::instmeth_iterator iter = PD->instmeth_begin(), + endIter = PD->instmeth_end() ; iter != endIter ; iter++) { + std::string TypeStr; + Context.getObjCEncodingForMethodDecl((*iter),TypeStr); + InstanceMethodNames.push_back( + GetAddrOfConstantString((*iter)->getSelector().getName())); + InstanceMethodTypes.push_back(GetAddrOfConstantString(TypeStr)); + } + // Collect information about class methods: + llvm::SmallVector ClassMethodNames; + llvm::SmallVector ClassMethodTypes; + for (ObjCProtocolDecl::classmeth_iterator iter = PD->classmeth_begin(), + endIter = PD->classmeth_end() ; iter != endIter ; iter++) { + std::string TypeStr; + Context.getObjCEncodingForMethodDecl((*iter),TypeStr); + ClassMethodNames.push_back( + GetAddrOfConstantString((*iter)->getSelector().getName())); + ClassMethodTypes.push_back(GetAddrOfConstantString(TypeStr)); + } + Runtime->GenerateProtocol(PD->getName(), Protocols, InstanceMethodNames, + InstanceMethodTypes, ClassMethodNames, ClassMethodTypes); +} + +void CodeGenModule::EmitObjCCategoryImpl(const ObjCCategoryImplDecl *OCD) { + + // Collect information about instance methods + llvm::SmallVector InstanceMethodNames; + llvm::SmallVector InstanceMethodTypes; + for (ObjCCategoryDecl::instmeth_iterator iter = OCD->instmeth_begin(), + endIter = OCD->instmeth_end() ; iter != endIter ; iter++) { + std::string TypeStr; + Context.getObjCEncodingForMethodDecl((*iter),TypeStr); + InstanceMethodNames.push_back( + GetAddrOfConstantString((*iter)->getSelector().getName())); + InstanceMethodTypes.push_back(GetAddrOfConstantString(TypeStr)); + } + + // Collect information about class methods + llvm::SmallVector ClassMethodNames; + llvm::SmallVector ClassMethodTypes; + for (ObjCCategoryDecl::classmeth_iterator iter = OCD->classmeth_begin(), + endIter = OCD->classmeth_end() ; iter != endIter ; iter++) { + std::string TypeStr; + Context.getObjCEncodingForMethodDecl((*iter),TypeStr); + ClassMethodNames.push_back( + GetAddrOfConstantString((*iter)->getSelector().getName())); + ClassMethodTypes.push_back(GetAddrOfConstantString(TypeStr)); + } + + // Collect the names of referenced protocols + llvm::SmallVector Protocols; + ObjCInterfaceDecl * ClassDecl = (ObjCInterfaceDecl*)OCD->getClassInterface(); + for (unsigned i=0 ; igetNumIntfRefProtocols() ; i++) + Protocols.push_back(ClassDecl->getReferencedProtocols()[i]->getName()); + + // Generate the category + Runtime->GenerateCategory(OCD->getClassInterface()->getName(), + OCD->getName(), InstanceMethodNames, InstanceMethodTypes, + ClassMethodNames, ClassMethodTypes, Protocols); +} + +void CodeGenModule::EmitObjCClassImplementation( + const ObjCImplementationDecl *OID) { + // Get the superclass name. + const ObjCInterfaceDecl * SCDecl = OID->getClassInterface()->getSuperClass(); + const char * SCName = NULL; + if (SCDecl) { + SCName = SCDecl->getName(); + } + + // Get the class name + ObjCInterfaceDecl * ClassDecl = (ObjCInterfaceDecl*)OID->getClassInterface(); + const char * ClassName = ClassDecl->getName(); + + // Get the size of instances. For runtimes that support late-bound instances + // this should probably be something different (size just of instance + // varaibles in this class, not superclasses?). + int instanceSize = 0; + const llvm::Type *ObjTy; + if (!Runtime->LateBoundIVars()) { + ObjTy = getTypes().ConvertType(Context.getObjCInterfaceType(ClassDecl)); + instanceSize = TheTargetData.getABITypeSize(ObjTy); + } + + // Collect information about instance variables. + llvm::SmallVector IvarNames; + llvm::SmallVector IvarTypes; + llvm::SmallVector IvarOffsets; + const llvm::StructLayout *Layout = + TheTargetData.getStructLayout(cast(ObjTy)); + ObjTy = llvm::PointerType::getUnqual(ObjTy); + for (ObjCInterfaceDecl::ivar_iterator iter = ClassDecl->ivar_begin(), + endIter = ClassDecl->ivar_end() ; iter != endIter ; iter++) { + // Store the name + IvarNames.push_back(GetAddrOfConstantString((*iter)->getName())); + // Get the type encoding for this ivar + std::string TypeStr; + llvm::SmallVector EncodingRecordTypes; + Context.getObjCEncodingForType((*iter)->getType(), TypeStr, + EncodingRecordTypes); + IvarTypes.push_back(GetAddrOfConstantString(TypeStr)); + // Get the offset + int offset = + (int)Layout->getElementOffset(getTypes().getLLVMFieldNo(*iter)); + IvarOffsets.push_back( + llvm::ConstantInt::get(llvm::Type::Int32Ty, offset)); + } + + // Collect information about instance methods + llvm::SmallVector InstanceMethodNames; + llvm::SmallVector InstanceMethodTypes; + for (ObjCImplementationDecl::instmeth_iterator iter = OID->instmeth_begin(), + endIter = OID->instmeth_end() ; iter != endIter ; iter++) { + std::string TypeStr; + Context.getObjCEncodingForMethodDecl((*iter),TypeStr); + InstanceMethodNames.push_back( + GetAddrOfConstantString((*iter)->getSelector().getName())); + InstanceMethodTypes.push_back(GetAddrOfConstantString(TypeStr)); + } + + // Collect information about class methods + llvm::SmallVector ClassMethodNames; + llvm::SmallVector ClassMethodTypes; + for (ObjCImplementationDecl::classmeth_iterator iter = OID->classmeth_begin(), + endIter = OID->classmeth_end() ; iter != endIter ; iter++) { + std::string TypeStr; + Context.getObjCEncodingForMethodDecl((*iter),TypeStr); + ClassMethodNames.push_back( + GetAddrOfConstantString((*iter)->getSelector().getName())); + ClassMethodTypes.push_back(GetAddrOfConstantString(TypeStr)); + } + // Collect the names of referenced protocols + llvm::SmallVector Protocols; + for (unsigned i = 0, e = ClassDecl->getNumIntfRefProtocols() ; i < e ; i++) + Protocols.push_back(ClassDecl->getReferencedProtocols()[i]->getName()); + + // Generate the category + Runtime->GenerateClass(ClassName, SCName, instanceSize, IvarNames, IvarTypes, + IvarOffsets, InstanceMethodNames, InstanceMethodTypes, ClassMethodNames, + ClassMethodTypes, Protocols); +} + void CodeGenModule::EmitFunction(const FunctionDecl *FD) { // If this is not a prototype, emit the body. @@ -623,6 +772,7 @@ llvm::Function *CodeGenModule::getMemSetFn() { return MemSetFn = getIntrinsic(IID); } +// FIXME: This needs moving into an Apple Objective-C runtime class llvm::Constant *CodeGenModule:: GetAddrOfConstantCFString(const std::string &str) { llvm::StringMapEntry &Entry = diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 992a3bf3c2..3e77526067 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -32,6 +32,9 @@ namespace clang { class ASTContext; class FunctionDecl; class ObjCMethodDecl; + class ObjCImplementationDecl; + class ObjCCategoryImplDecl; + class ObjCProtocolDecl; class Decl; class Expr; class Stmt; @@ -114,6 +117,9 @@ public: void EmitStatics(void); void EmitObjCMethod(const ObjCMethodDecl *OMD); + void EmitObjCCategoryImpl(const ObjCCategoryImplDecl *OCD); + void EmitObjCClassImplementation(const ObjCImplementationDecl *OID); + void EmitObjCProtocolImplementation(const ObjCProtocolDecl *PD); void EmitFunction(const FunctionDecl *FD); void EmitGlobalVar(const VarDecl *D); void EmitGlobalVarInit(const VarDecl *D); diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index 1184307b86..b37b8d3f01 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -67,6 +67,24 @@ namespace { if (FunctionDecl *FD = dyn_cast(D)) { Builder->EmitFunction(FD); + } else if (isa(D)){ + //Forward declaration. Only used for type checking. + } else if (ObjCProtocolDecl *PD = dyn_cast(D)){ + // Generate Protocol object. + Builder->EmitObjCProtocolImplementation(PD); + } else if (isa(D)){ + //Only used for typechecking. + } else if (ObjCCategoryImplDecl *OCD = dyn_cast(D)){ + // Generate methods, attach to category structure + Builder->EmitObjCCategoryImpl(OCD); + } else if (ObjCImplementationDecl * OID = + dyn_cast(D)){ + // Generate methods, attach to class structure + Builder->EmitObjCClassImplementation(OID); + } else if (isa(D)){ + // Ignore - generated when the implementation decl is CodeGen'd + } else if (ObjCMethodDecl *OMD = dyn_cast(D)){ + Builder->EmitObjCMethod(OMD); } else if (VarDecl *VD = dyn_cast(D)) { if (VD->isFileVarDecl()) Builder->EmitGlobalVarDeclarator(VD); -- 2.40.0