//===------- CGObjCMac.cpp - Interface to Apple Objective-C Runtime -------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This provides Objective-C code generation targetting the Apple runtime. // //===----------------------------------------------------------------------===// #include "CGObjCRuntime.h" #include "CGRecordLayout.h" #include "CodeGenModule.h" #include "CodeGenFunction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/LangOptions.h" #include "clang/CodeGen/CodeGenOptions.h" #include "llvm/Intrinsics.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetData.h" #include using namespace clang; using namespace CodeGen; // Common CGObjCRuntime functions, these don't belong here, but they // don't belong in CGObjCRuntime either so we will live with it for // now. static uint64_t LookupFieldBitOffset(CodeGen::CodeGenModule &CGM, const ObjCInterfaceDecl *OID, const ObjCImplementationDecl *ID, const ObjCIvarDecl *Ivar) { const ObjCInterfaceDecl *Container = Ivar->getContainingInterface(); // FIXME: We should eliminate the need to have ObjCImplementationDecl passed // in here; it should never be necessary because that should be the lexical // decl context for the ivar. // If we know have an implementation (and the ivar is in it) then // look up in the implementation layout. const ASTRecordLayout *RL; if (ID && ID->getClassInterface() == Container) RL = &CGM.getContext().getASTObjCImplementationLayout(ID); else RL = &CGM.getContext().getASTObjCInterfaceLayout(Container); // Compute field index. // // FIXME: The index here is closely tied to how ASTContext::getObjCLayout is // implemented. This should be fixed to get the information from the layout // directly. unsigned Index = 0; llvm::SmallVector Ivars; CGM.getContext().ShallowCollectObjCIvars(Container, Ivars); for (unsigned k = 0, e = Ivars.size(); k != e; ++k) { if (Ivar == Ivars[k]) break; ++Index; } assert(Index != Ivars.size() && "Ivar is not inside container!"); return RL->getFieldOffset(Index); } uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM, const ObjCInterfaceDecl *OID, const ObjCIvarDecl *Ivar) { return LookupFieldBitOffset(CGM, OID, 0, Ivar) / 8; } uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM, const ObjCImplementationDecl *OID, const ObjCIvarDecl *Ivar) { return LookupFieldBitOffset(CGM, OID->getClassInterface(), OID, Ivar) / 8; } LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, const ObjCInterfaceDecl *OID, llvm::Value *BaseValue, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers, llvm::Value *Offset) { // Compute (type*) ( (char *) BaseValue + Offset) const llvm::Type *I8Ptr = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); QualType IvarTy = Ivar->getType(); const llvm::Type *LTy = CGF.CGM.getTypes().ConvertTypeForMem(IvarTy); llvm::Value *V = CGF.Builder.CreateBitCast(BaseValue, I8Ptr); V = CGF.Builder.CreateGEP(V, Offset, "add.ptr"); V = CGF.Builder.CreateBitCast(V, llvm::PointerType::getUnqual(LTy)); Qualifiers Quals = CGF.MakeQualifiers(IvarTy); Quals.addCVRQualifiers(CVRQualifiers); if (!Ivar->isBitField()) return LValue::MakeAddr(V, Quals); // We need to compute the bit offset for the bit-field, the offset is to the // byte. Note, there is a subtle invariant here: we can only call this routine // on non-synthesized ivars but we may be called for synthesized ivars. // However, a synthesized ivar can never be a bit-field, so this is safe. uint64_t BitOffset = LookupFieldBitOffset(CGF.CGM, OID, 0, Ivar) % 8; uint64_t BitFieldSize = Ivar->getBitWidth()->EvaluateAsInt(CGF.getContext()).getZExtValue(); // Allocate a new CGBitFieldInfo object to describe this access. // // FIXME: This is incredibly wasteful, these should be uniqued or part of some // layout object. However, this is blocked on other cleanups to the // Objective-C code, so for now we just live with allocating a bunch of these // objects. // We always construct a single, possibly unaligned, access for this case. CGBitFieldInfo::AccessInfo AI; AI.FieldIndex = 0; AI.FieldByteOffset = 0; AI.FieldBitStart = BitOffset; AI.AccessWidth = CGF.CGM.getContext().getTypeSize(IvarTy); AI.AccessAlignment = 0; AI.TargetBitOffset = 0; AI.TargetBitWidth = BitFieldSize; CGBitFieldInfo *Info = new (CGF.CGM.getContext()) CGBitFieldInfo(BitFieldSize, 1, &AI, IvarTy->isSignedIntegerType()); // FIXME: We need to set a very conservative alignment on this, or make sure // that the runtime is doing the right thing. return LValue::MakeBitfield(V, *Info, Quals.getCVRQualifiers()); } /// namespace { typedef std::vector ConstantVector; // FIXME: We should find a nicer way to make the labels for metadata, string // concatenation is lame. class ObjCCommonTypesHelper { protected: llvm::LLVMContext &VMContext; private: llvm::Constant *getMessageSendFn() const { // id objc_msgSend (id, SEL, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSend"); } llvm::Constant *getMessageSendStretFn() const { // id objc_msgSend_stret (id, SEL, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, true), "objc_msgSend_stret"); } llvm::Constant *getMessageSendFpretFn() const { // FIXME: This should be long double on x86_64? // [double | long double] objc_msgSend_fpret(id self, SEL op, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get( llvm::Type::getDoubleTy(VMContext), Params, true), "objc_msgSend_fpret"); } llvm::Constant *getMessageSendSuperFn() const { // id objc_msgSendSuper(struct objc_super *super, SEL op, ...) const char *SuperName = "objc_msgSendSuper"; std::vector Params; Params.push_back(SuperPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), SuperName); } llvm::Constant *getMessageSendSuperFn2() const { // id objc_msgSendSuper2(struct objc_super *super, SEL op, ...) const char *SuperName = "objc_msgSendSuper2"; std::vector Params; Params.push_back(SuperPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), SuperName); } llvm::Constant *getMessageSendSuperStretFn() const { // void objc_msgSendSuper_stret(void * stretAddr, struct objc_super *super, // SEL op, ...) std::vector Params; Params.push_back(Int8PtrTy); Params.push_back(SuperPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, true), "objc_msgSendSuper_stret"); } llvm::Constant *getMessageSendSuperStretFn2() const { // void objc_msgSendSuper2_stret(void * stretAddr, struct objc_super *super, // SEL op, ...) std::vector Params; Params.push_back(Int8PtrTy); Params.push_back(SuperPtrTy); Params.push_back(SelectorPtrTy); return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, true), "objc_msgSendSuper2_stret"); } llvm::Constant *getMessageSendSuperFpretFn() const { // There is no objc_msgSendSuper_fpret? How can that work? return getMessageSendSuperFn(); } llvm::Constant *getMessageSendSuperFpretFn2() const { // There is no objc_msgSendSuper_fpret? How can that work? return getMessageSendSuperFn2(); } protected: CodeGen::CodeGenModule &CGM; public: const llvm::Type *ShortTy, *IntTy, *LongTy, *LongLongTy; const llvm::Type *Int8PtrTy; /// ObjectPtrTy - LLVM type for object handles (typeof(id)) const llvm::Type *ObjectPtrTy; /// PtrObjectPtrTy - LLVM type for id * const llvm::Type *PtrObjectPtrTy; /// SelectorPtrTy - LLVM type for selector handles (typeof(SEL)) const llvm::Type *SelectorPtrTy; /// ProtocolPtrTy - LLVM type for external protocol handles /// (typeof(Protocol)) const llvm::Type *ExternalProtocolPtrTy; // SuperCTy - clang type for struct objc_super. QualType SuperCTy; // SuperPtrCTy - clang type for struct objc_super *. QualType SuperPtrCTy; /// SuperTy - LLVM type for struct objc_super. const llvm::StructType *SuperTy; /// SuperPtrTy - LLVM type for struct objc_super *. const llvm::Type *SuperPtrTy; /// PropertyTy - LLVM type for struct objc_property (struct _prop_t /// in GCC parlance). const llvm::StructType *PropertyTy; /// PropertyListTy - LLVM type for struct objc_property_list /// (_prop_list_t in GCC parlance). const llvm::StructType *PropertyListTy; /// PropertyListPtrTy - LLVM type for struct objc_property_list*. const llvm::Type *PropertyListPtrTy; // MethodTy - LLVM type for struct objc_method. const llvm::StructType *MethodTy; /// CacheTy - LLVM type for struct objc_cache. const llvm::Type *CacheTy; /// CachePtrTy - LLVM type for struct objc_cache *. const llvm::Type *CachePtrTy; llvm::Constant *getGetPropertyFn() { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); // id objc_getProperty (id, SEL, ptrdiff_t, bool) llvm::SmallVector Params; CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType()); CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType()); Params.push_back(IdType); Params.push_back(SelType); Params.push_back(Ctx.LongTy); Params.push_back(Ctx.BoolTy); const llvm::FunctionType *FTy = Types.GetFunctionType(Types.getFunctionInfo(IdType, Params, FunctionType::ExtInfo()), false); return CGM.CreateRuntimeFunction(FTy, "objc_getProperty"); } llvm::Constant *getSetPropertyFn() { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); // void objc_setProperty (id, SEL, ptrdiff_t, id, bool, bool) llvm::SmallVector Params; CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType()); CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType()); Params.push_back(IdType); Params.push_back(SelType); Params.push_back(Ctx.LongTy); Params.push_back(IdType); Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); const llvm::FunctionType *FTy = Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params, FunctionType::ExtInfo()), false); return CGM.CreateRuntimeFunction(FTy, "objc_setProperty"); } llvm::Constant *getCopyStructFn() { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); // void objc_copyStruct (void *, const void *, size_t, bool, bool) llvm::SmallVector Params; Params.push_back(Ctx.VoidPtrTy); Params.push_back(Ctx.VoidPtrTy); Params.push_back(Ctx.LongTy); Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); const llvm::FunctionType *FTy = Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params, FunctionType::ExtInfo()), false); return CGM.CreateRuntimeFunction(FTy, "objc_copyStruct"); } llvm::Constant *getEnumerationMutationFn() { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); // void objc_enumerationMutation (id) llvm::SmallVector Params; Params.push_back(Ctx.getCanonicalParamType(Ctx.getObjCIdType())); const llvm::FunctionType *FTy = Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params, FunctionType::ExtInfo()), false); return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } /// GcReadWeakFn -- LLVM objc_read_weak (id *src) function. llvm::Constant *getGcReadWeakFn() { // id objc_read_weak (id *) std::vector Args; Args.push_back(ObjectPtrTy->getPointerTo()); llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_read_weak"); } /// GcAssignWeakFn -- LLVM objc_assign_weak function. llvm::Constant *getGcAssignWeakFn() { // id objc_assign_weak (id, id *) std::vector Args(1, ObjectPtrTy); Args.push_back(ObjectPtrTy->getPointerTo()); llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_weak"); } /// GcAssignGlobalFn -- LLVM objc_assign_global function. llvm::Constant *getGcAssignGlobalFn() { // id objc_assign_global(id, id *) std::vector Args(1, ObjectPtrTy); Args.push_back(ObjectPtrTy->getPointerTo()); llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_global"); } /// GcAssignIvarFn -- LLVM objc_assign_ivar function. llvm::Constant *getGcAssignIvarFn() { // id objc_assign_ivar(id, id *, ptrdiff_t) std::vector Args(1, ObjectPtrTy); Args.push_back(ObjectPtrTy->getPointerTo()); Args.push_back(LongTy); llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_ivar"); } /// GcMemmoveCollectableFn -- LLVM objc_memmove_collectable function. llvm::Constant *GcMemmoveCollectableFn() { // void *objc_memmove_collectable(void *dst, const void *src, size_t size) std::vector Args(1, Int8PtrTy); Args.push_back(Int8PtrTy); Args.push_back(LongTy); llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_memmove_collectable"); } /// GcAssignStrongCastFn -- LLVM objc_assign_strongCast function. llvm::Constant *getGcAssignStrongCastFn() { // id objc_assign_global(id, id *) std::vector Args(1, ObjectPtrTy); Args.push_back(ObjectPtrTy->getPointerTo()); llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_strongCast"); } /// ExceptionThrowFn - LLVM objc_exception_throw function. llvm::Constant *getExceptionThrowFn() { // void objc_exception_throw(id) std::vector Args(1, ObjectPtrTy); llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_exception_throw"); } /// SyncEnterFn - LLVM object_sync_enter function. llvm::Constant *getSyncEnterFn() { // void objc_sync_enter (id) std::vector Args(1, ObjectPtrTy); llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_sync_enter"); } /// SyncExitFn - LLVM object_sync_exit function. llvm::Constant *getSyncExitFn() { // void objc_sync_exit (id) std::vector Args(1, ObjectPtrTy); llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Args, false); return CGM.CreateRuntimeFunction(FTy, "objc_sync_exit"); } llvm::Constant *getSendFn(bool IsSuper) const { return IsSuper ? getMessageSendSuperFn() : getMessageSendFn(); } llvm::Constant *getSendFn2(bool IsSuper) const { return IsSuper ? getMessageSendSuperFn2() : getMessageSendFn(); } llvm::Constant *getSendStretFn(bool IsSuper) const { return IsSuper ? getMessageSendSuperStretFn() : getMessageSendStretFn(); } llvm::Constant *getSendStretFn2(bool IsSuper) const { return IsSuper ? getMessageSendSuperStretFn2() : getMessageSendStretFn(); } llvm::Constant *getSendFpretFn(bool IsSuper) const { return IsSuper ? getMessageSendSuperFpretFn() : getMessageSendFpretFn(); } llvm::Constant *getSendFpretFn2(bool IsSuper) const { return IsSuper ? getMessageSendSuperFpretFn2() : getMessageSendFpretFn(); } ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm); ~ObjCCommonTypesHelper(){} }; /// ObjCTypesHelper - Helper class that encapsulates lazy /// construction of varies types used during ObjC generation. class ObjCTypesHelper : public ObjCCommonTypesHelper { public: /// SymtabTy - LLVM type for struct objc_symtab. const llvm::StructType *SymtabTy; /// SymtabPtrTy - LLVM type for struct objc_symtab *. const llvm::Type *SymtabPtrTy; /// ModuleTy - LLVM type for struct objc_module. const llvm::StructType *ModuleTy; /// ProtocolTy - LLVM type for struct objc_protocol. const llvm::StructType *ProtocolTy; /// ProtocolPtrTy - LLVM type for struct objc_protocol *. const llvm::Type *ProtocolPtrTy; /// ProtocolExtensionTy - LLVM type for struct /// objc_protocol_extension. const llvm::StructType *ProtocolExtensionTy; /// ProtocolExtensionTy - LLVM type for struct /// objc_protocol_extension *. const llvm::Type *ProtocolExtensionPtrTy; /// MethodDescriptionTy - LLVM type for struct /// objc_method_description. const llvm::StructType *MethodDescriptionTy; /// MethodDescriptionListTy - LLVM type for struct /// objc_method_description_list. const llvm::StructType *MethodDescriptionListTy; /// MethodDescriptionListPtrTy - LLVM type for struct /// objc_method_description_list *. const llvm::Type *MethodDescriptionListPtrTy; /// ProtocolListTy - LLVM type for struct objc_property_list. const llvm::Type *ProtocolListTy; /// ProtocolListPtrTy - LLVM type for struct objc_property_list*. const llvm::Type *ProtocolListPtrTy; /// CategoryTy - LLVM type for struct objc_category. const llvm::StructType *CategoryTy; /// ClassTy - LLVM type for struct objc_class. const llvm::StructType *ClassTy; /// ClassPtrTy - LLVM type for struct objc_class *. const llvm::Type *ClassPtrTy; /// ClassExtensionTy - LLVM type for struct objc_class_ext. const llvm::StructType *ClassExtensionTy; /// ClassExtensionPtrTy - LLVM type for struct objc_class_ext *. const llvm::Type *ClassExtensionPtrTy; // IvarTy - LLVM type for struct objc_ivar. const llvm::StructType *IvarTy; /// IvarListTy - LLVM type for struct objc_ivar_list. const llvm::Type *IvarListTy; /// IvarListPtrTy - LLVM type for struct objc_ivar_list *. const llvm::Type *IvarListPtrTy; /// MethodListTy - LLVM type for struct objc_method_list. const llvm::Type *MethodListTy; /// MethodListPtrTy - LLVM type for struct objc_method_list *. const llvm::Type *MethodListPtrTy; /// ExceptionDataTy - LLVM type for struct _objc_exception_data. const llvm::Type *ExceptionDataTy; /// ExceptionTryEnterFn - LLVM objc_exception_try_enter function. llvm::Constant *getExceptionTryEnterFn() { std::vector Params; Params.push_back(llvm::PointerType::getUnqual(ExceptionDataTy)); return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, false), "objc_exception_try_enter"); } /// ExceptionTryExitFn - LLVM objc_exception_try_exit function. llvm::Constant *getExceptionTryExitFn() { std::vector Params; Params.push_back(llvm::PointerType::getUnqual(ExceptionDataTy)); return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, false), "objc_exception_try_exit"); } /// ExceptionExtractFn - LLVM objc_exception_extract function. llvm::Constant *getExceptionExtractFn() { std::vector Params; Params.push_back(llvm::PointerType::getUnqual(ExceptionDataTy)); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, false), "objc_exception_extract"); } /// ExceptionMatchFn - LLVM objc_exception_match function. llvm::Constant *getExceptionMatchFn() { std::vector Params; Params.push_back(ClassPtrTy); Params.push_back(ObjectPtrTy); return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getInt32Ty(VMContext), Params, false), "objc_exception_match"); } /// SetJmpFn - LLVM _setjmp function. llvm::Constant *getSetJmpFn() { std::vector Params; Params.push_back(llvm::Type::getInt32PtrTy(VMContext)); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getInt32Ty(VMContext), Params, false), "_setjmp"); } public: ObjCTypesHelper(CodeGen::CodeGenModule &cgm); ~ObjCTypesHelper() {} }; /// ObjCNonFragileABITypesHelper - will have all types needed by objective-c's /// modern abi class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { public: // MethodListnfABITy - LLVM for struct _method_list_t const llvm::StructType *MethodListnfABITy; // MethodListnfABIPtrTy - LLVM for struct _method_list_t* const llvm::Type *MethodListnfABIPtrTy; // ProtocolnfABITy = LLVM for struct _protocol_t const llvm::StructType *ProtocolnfABITy; // ProtocolnfABIPtrTy = LLVM for struct _protocol_t* const llvm::Type *ProtocolnfABIPtrTy; // ProtocolListnfABITy - LLVM for struct _objc_protocol_list const llvm::StructType *ProtocolListnfABITy; // ProtocolListnfABIPtrTy - LLVM for struct _objc_protocol_list* const llvm::Type *ProtocolListnfABIPtrTy; // ClassnfABITy - LLVM for struct _class_t const llvm::StructType *ClassnfABITy; // ClassnfABIPtrTy - LLVM for struct _class_t* const llvm::Type *ClassnfABIPtrTy; // IvarnfABITy - LLVM for struct _ivar_t const llvm::StructType *IvarnfABITy; // IvarListnfABITy - LLVM for struct _ivar_list_t const llvm::StructType *IvarListnfABITy; // IvarListnfABIPtrTy = LLVM for struct _ivar_list_t* const llvm::Type *IvarListnfABIPtrTy; // ClassRonfABITy - LLVM for struct _class_ro_t const llvm::StructType *ClassRonfABITy; // ImpnfABITy - LLVM for id (*)(id, SEL, ...) const llvm::Type *ImpnfABITy; // CategorynfABITy - LLVM for struct _category_t const llvm::StructType *CategorynfABITy; // New types for nonfragile abi messaging. // MessageRefTy - LLVM for: // struct _message_ref_t { // IMP messenger; // SEL name; // }; const llvm::StructType *MessageRefTy; // MessageRefCTy - clang type for struct _message_ref_t QualType MessageRefCTy; // MessageRefPtrTy - LLVM for struct _message_ref_t* const llvm::Type *MessageRefPtrTy; // MessageRefCPtrTy - clang type for struct _message_ref_t* QualType MessageRefCPtrTy; // MessengerTy - Type of the messenger (shown as IMP above) const llvm::FunctionType *MessengerTy; // SuperMessageRefTy - LLVM for: // struct _super_message_ref_t { // SUPER_IMP messenger; // SEL name; // }; const llvm::StructType *SuperMessageRefTy; // SuperMessageRefPtrTy - LLVM for struct _super_message_ref_t* const llvm::Type *SuperMessageRefPtrTy; llvm::Constant *getMessageSendFixupFn() { // id objc_msgSend_fixup(id, struct message_ref_t*, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(MessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSend_fixup"); } llvm::Constant *getMessageSendFpretFixupFn() { // id objc_msgSend_fpret_fixup(id, struct message_ref_t*, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(MessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSend_fpret_fixup"); } llvm::Constant *getMessageSendStretFixupFn() { // id objc_msgSend_stret_fixup(id, struct message_ref_t*, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(MessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSend_stret_fixup"); } llvm::Constant *getMessageSendIdFixupFn() { // id objc_msgSendId_fixup(id, struct message_ref_t*, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(MessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSendId_fixup"); } llvm::Constant *getMessageSendIdStretFixupFn() { // id objc_msgSendId_stret_fixup(id, struct message_ref_t*, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(MessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSendId_stret_fixup"); } llvm::Constant *getMessageSendSuper2FixupFn() { // id objc_msgSendSuper2_fixup (struct objc_super *, // struct _super_message_ref_t*, ...) std::vector Params; Params.push_back(SuperPtrTy); Params.push_back(SuperMessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSendSuper2_fixup"); } llvm::Constant *getMessageSendSuper2StretFixupFn() { // id objc_msgSendSuper2_stret_fixup(struct objc_super *, // struct _super_message_ref_t*, ...) std::vector Params; Params.push_back(SuperPtrTy); Params.push_back(SuperMessageRefPtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, Params, true), "objc_msgSendSuper2_stret_fixup"); } /// EHPersonalityPtr - LLVM value for an i8* to the Objective-C /// exception personality function. llvm::Value *getEHPersonalityPtr() { llvm::Constant *Personality = CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getInt32Ty(VMContext), true), "__objc_personality_v0"); return llvm::ConstantExpr::getBitCast(Personality, Int8PtrTy); } llvm::Constant *getUnwindResumeOrRethrowFn() { std::vector Params; Params.push_back(Int8PtrTy); return CGM.CreateRuntimeFunction( llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), Params, false), (CGM.getLangOptions().SjLjExceptions ? "_Unwind_SjLj_Resume" : "_Unwind_Resume_or_Rethrow")); } llvm::Constant *getObjCEndCatchFn() { return CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::getVoidTy(VMContext), false), "objc_end_catch"); } llvm::Constant *getObjCBeginCatchFn() { std::vector Params; Params.push_back(Int8PtrTy); return CGM.CreateRuntimeFunction(llvm::FunctionType::get(Int8PtrTy, Params, false), "objc_begin_catch"); } const llvm::StructType *EHTypeTy; const llvm::Type *EHTypePtrTy; ObjCNonFragileABITypesHelper(CodeGen::CodeGenModule &cgm); ~ObjCNonFragileABITypesHelper(){} }; class CGObjCCommonMac : public CodeGen::CGObjCRuntime { public: // FIXME - accessibility class GC_IVAR { public: unsigned ivar_bytepos; unsigned ivar_size; GC_IVAR(unsigned bytepos = 0, unsigned size = 0) : ivar_bytepos(bytepos), ivar_size(size) {} // Allow sorting based on byte pos. bool operator<(const GC_IVAR &b) const { return ivar_bytepos < b.ivar_bytepos; } }; class SKIP_SCAN { public: unsigned skip; unsigned scan; SKIP_SCAN(unsigned _skip = 0, unsigned _scan = 0) : skip(_skip), scan(_scan) {} }; protected: CodeGen::CodeGenModule &CGM; llvm::LLVMContext &VMContext; // FIXME! May not be needing this after all. unsigned ObjCABI; // gc ivar layout bitmap calculation helper caches. llvm::SmallVector SkipIvars; llvm::SmallVector IvarsInfo; /// LazySymbols - Symbols to generate a lazy reference for. See /// DefinedSymbols and FinishModule(). llvm::SetVector LazySymbols; /// DefinedSymbols - External symbols which are defined by this /// module. The symbols in this list and LazySymbols are used to add /// special linker symbols which ensure that Objective-C modules are /// linked properly. llvm::SetVector DefinedSymbols; /// ClassNames - uniqued class names. llvm::DenseMap ClassNames; /// MethodVarNames - uniqued method variable names. llvm::DenseMap MethodVarNames; /// MethodVarTypes - uniqued method type signatures. We have to use /// a StringMap here because have no other unique reference. llvm::StringMap MethodVarTypes; /// MethodDefinitions - map of methods which have been defined in /// this translation unit. llvm::DenseMap MethodDefinitions; /// PropertyNames - uniqued method variable names. llvm::DenseMap PropertyNames; /// ClassReferences - uniqued class references. llvm::DenseMap ClassReferences; /// SelectorReferences - uniqued selector references. llvm::DenseMap SelectorReferences; /// Protocols - Protocols for which an objc_protocol structure has /// been emitted. Forward declarations are handled by creating an /// empty structure whose initializer is filled in when/if defined. llvm::DenseMap Protocols; /// DefinedProtocols - Protocols which have actually been /// defined. We should not need this, see FIXME in GenerateProtocol. llvm::DenseSet DefinedProtocols; /// DefinedClasses - List of defined classes. std::vector DefinedClasses; /// DefinedNonLazyClasses - List of defined "non-lazy" classes. std::vector DefinedNonLazyClasses; /// DefinedCategories - List of defined categories. std::vector DefinedCategories; /// DefinedNonLazyCategories - List of defined "non-lazy" categories. std::vector DefinedNonLazyCategories; /// GetNameForMethod - Return a name for the given method. /// \param[out] NameOut - The return value. void GetNameForMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD, llvm::SmallVectorImpl &NameOut); /// GetMethodVarName - Return a unique constant for the given /// selector's name. The return value has type char *. llvm::Constant *GetMethodVarName(Selector Sel); llvm::Constant *GetMethodVarName(IdentifierInfo *Ident); llvm::Constant *GetMethodVarName(const std::string &Name); /// GetMethodVarType - Return a unique constant for the given /// selector's name. The return value has type char *. // FIXME: This is a horrible name. llvm::Constant *GetMethodVarType(const ObjCMethodDecl *D); llvm::Constant *GetMethodVarType(const FieldDecl *D); /// GetPropertyName - Return a unique constant for the given /// name. The return value has type char *. llvm::Constant *GetPropertyName(IdentifierInfo *Ident); // FIXME: This can be dropped once string functions are unified. llvm::Constant *GetPropertyTypeString(const ObjCPropertyDecl *PD, const Decl *Container); /// GetClassName - Return a unique constant for the given selector's /// name. The return value has type char *. llvm::Constant *GetClassName(IdentifierInfo *Ident); /// BuildIvarLayout - Builds ivar layout bitmap for the class /// implementation for the __strong or __weak case. /// llvm::Constant *BuildIvarLayout(const ObjCImplementationDecl *OI, bool ForStrongLayout); void BuildAggrIvarRecordLayout(const RecordType *RT, unsigned int BytePos, bool ForStrongLayout, bool &HasUnion); void BuildAggrIvarLayout(const ObjCImplementationDecl *OI, const llvm::StructLayout *Layout, const RecordDecl *RD, const llvm::SmallVectorImpl &RecFields, unsigned int BytePos, bool ForStrongLayout, bool &HasUnion); /// GetIvarLayoutName - Returns a unique constant for the given /// ivar layout bitmap. llvm::Constant *GetIvarLayoutName(IdentifierInfo *Ident, const ObjCCommonTypesHelper &ObjCTypes); /// EmitPropertyList - Emit the given property list. The return /// value has type PropertyListPtrTy. llvm::Constant *EmitPropertyList(llvm::Twine Name, const Decl *Container, const ObjCContainerDecl *OCD, const ObjCCommonTypesHelper &ObjCTypes); /// PushProtocolProperties - Push protocol's property on the input stack. void PushProtocolProperties(llvm::SmallPtrSet &PropertySet, std::vector &Properties, const Decl *Container, const ObjCProtocolDecl *PROTO, const ObjCCommonTypesHelper &ObjCTypes); /// GetProtocolRef - Return a reference to the internal protocol /// description, creating an empty one if it has not been /// defined. The return value has type ProtocolPtrTy. llvm::Constant *GetProtocolRef(const ObjCProtocolDecl *PD); /// CreateMetadataVar - Create a global variable with internal /// linkage for use by the Objective-C runtime. /// /// This is a convenience wrapper which not only creates the /// variable, but also sets the section and alignment and adds the /// global to the "llvm.used" list. /// /// \param Name - The variable name. /// \param Init - The variable initializer; this is also used to /// define the type of the variable. /// \param Section - The section the variable should go into, or 0. /// \param Align - The alignment for the variable, or 0. /// \param AddToUsed - Whether the variable should be added to /// "llvm.used". llvm::GlobalVariable *CreateMetadataVar(llvm::Twine Name, llvm::Constant *Init, const char *Section, unsigned Align, bool AddToUsed); CodeGen::RValue EmitLegacyMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, llvm::Value *Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, const CallArgList &CallArgs, const ObjCMethodDecl *OMD, const ObjCCommonTypesHelper &ObjCTypes); /// EmitImageInfo - Emit the image info marker used to encode some module /// level information. void EmitImageInfo(); public: CGObjCCommonMac(CodeGen::CodeGenModule &cgm) : CGM(cgm), VMContext(cgm.getLLVMContext()) { } virtual llvm::Constant *GenerateConstantString(const StringLiteral *SL); virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD=0); virtual void GenerateProtocol(const ObjCProtocolDecl *PD); /// GetOrEmitProtocol - Get the protocol object for the given /// declaration, emitting it if necessary. The return value has type /// ProtocolPtrTy. virtual llvm::Constant *GetOrEmitProtocol(const ObjCProtocolDecl *PD)=0; /// GetOrEmitProtocolRef - Get a forward reference to the protocol /// object for the given declaration, emitting it if needed. These /// forward references will be filled in with empty bodies if no /// definition is seen. The return value has type ProtocolPtrTy. virtual llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD)=0; }; class CGObjCMac : public CGObjCCommonMac { private: ObjCTypesHelper ObjCTypes; /// EmitModuleInfo - Another marker encoding module level /// information. void EmitModuleInfo(); /// EmitModuleSymols - Emit module symbols, the list of defined /// classes and categories. The result has type SymtabPtrTy. llvm::Constant *EmitModuleSymbols(); /// FinishModule - Write out global data structures at the end of /// processing a translation unit. void FinishModule(); /// EmitClassExtension - Generate the class extension structure used /// to store the weak ivar layout and properties. The return value /// has type ClassExtensionPtrTy. llvm::Constant *EmitClassExtension(const ObjCImplementationDecl *ID); /// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given class. llvm::Value *EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); /// EmitSuperClassRef - Emits reference to class's main metadata class. llvm::Value *EmitSuperClassRef(const ObjCInterfaceDecl *ID); CodeGen::RValue EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, const CallArgList &CallArgs); /// EmitIvarList - Emit the ivar list for the given /// implementation. If ForClass is true the list of class ivars /// (i.e. metaclass ivars) is emitted, otherwise the list of /// interface ivars will be emitted. The return value has type /// IvarListPtrTy. llvm::Constant *EmitIvarList(const ObjCImplementationDecl *ID, bool ForClass); /// 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. The return value has type ClassPtrTy. llvm::Constant *EmitMetaClass(const ObjCImplementationDecl *ID, llvm::Constant *Protocols, const ConstantVector &Methods); llvm::Constant *GetMethodConstant(const ObjCMethodDecl *MD); llvm::Constant *GetMethodDescriptionConstant(const ObjCMethodDecl *MD); /// EmitMethodList - Emit the method list for the given /// implementation. The return value has type MethodListPtrTy. llvm::Constant *EmitMethodList(llvm::Twine Name, const char *Section, const ConstantVector &Methods); /// EmitMethodDescList - Emit a method description list for a list of /// method declarations. /// - TypeName: The name for the type containing the methods. /// - IsProtocol: True iff these methods are for a protocol. /// - ClassMethds: True iff these are class methods. /// - Required: When true, only "required" methods are /// listed. Similarly, when false only "optional" methods are /// listed. For classes this should always be true. /// - begin, end: The method list to output. /// /// The return value has type MethodDescriptionListPtrTy. llvm::Constant *EmitMethodDescList(llvm::Twine Name, const char *Section, const ConstantVector &Methods); /// GetOrEmitProtocol - Get the protocol object for the given /// declaration, emitting it if necessary. The return value has type /// ProtocolPtrTy. virtual llvm::Constant *GetOrEmitProtocol(const ObjCProtocolDecl *PD); /// GetOrEmitProtocolRef - Get a forward reference to the protocol /// object for the given declaration, emitting it if needed. These /// forward references will be filled in with empty bodies if no /// definition is seen. The return value has type ProtocolPtrTy. virtual llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD); /// EmitProtocolExtension - Generate the protocol extension /// structure used to store optional instance and class methods, and /// protocol properties. The return value has type /// ProtocolExtensionPtrTy. llvm::Constant * EmitProtocolExtension(const ObjCProtocolDecl *PD, const ConstantVector &OptInstanceMethods, const ConstantVector &OptClassMethods); /// EmitProtocolList - Generate the list of referenced /// protocols. The return value has type ProtocolListPtrTy. llvm::Constant *EmitProtocolList(llvm::Twine Name, ObjCProtocolDecl::protocol_iterator begin, ObjCProtocolDecl::protocol_iterator end); /// EmitSelector - Return a Value*, of type ObjCTypes.SelectorPtrTy, /// for the given selector. llvm::Value *EmitSelector(CGBuilderTy &Builder, Selector Sel); public: CGObjCMac(CodeGen::CodeGenModule &cgm); virtual llvm::Function *ModuleInitFunction(); virtual CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Receiver, const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method); virtual CodeGen::RValue GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, const ObjCInterfaceDecl *Class, bool isCategoryImpl, llvm::Value *Receiver, bool IsClassMessage, const CallArgList &CallArgs, const ObjCMethodDecl *Method); virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel); /// The NeXT/Apple runtimes do not support typed selectors; just emit an /// untyped one. virtual llvm::Value *GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method); virtual void GenerateCategory(const ObjCCategoryImplDecl *CMD); virtual void GenerateClass(const ObjCImplementationDecl *ClassDecl); virtual llvm::Value *GenerateProtocolRef(CGBuilderTy &Builder, const ObjCProtocolDecl *PD); virtual llvm::Constant *GetPropertyGetFunction(); virtual llvm::Constant *GetPropertySetFunction(); virtual llvm::Constant *GetCopyStructFunction(); virtual llvm::Constant *EnumerationMutationFunction(); virtual void EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S); virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S); virtual llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, llvm::Value *AddrWeakObj); virtual void EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst); virtual void EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dest); virtual void EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dest, llvm::Value *ivarOffset); virtual void EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dest); virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *dest, llvm::Value *src, QualType Ty); virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy, llvm::Value *BaseValue, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers); virtual llvm::Value *EmitIvarOffset(CodeGen::CodeGenFunction &CGF, const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); }; class CGObjCNonFragileABIMac : public CGObjCCommonMac { private: ObjCNonFragileABITypesHelper ObjCTypes; llvm::GlobalVariable* ObjCEmptyCacheVar; llvm::GlobalVariable* ObjCEmptyVtableVar; /// SuperClassReferences - uniqued super class references. llvm::DenseMap SuperClassReferences; /// MetaClassReferences - uniqued meta class references. llvm::DenseMap MetaClassReferences; /// EHTypeReferences - uniqued class ehtype references. llvm::DenseMap EHTypeReferences; /// NonLegacyDispatchMethods - List of methods for which we do *not* generate /// legacy messaging dispatch. llvm::DenseSet NonLegacyDispatchMethods; /// DefinedMetaClasses - List of defined meta-classes. std::vector DefinedMetaClasses; /// LegacyDispatchedSelector - Returns true if SEL is not in the list of /// NonLegacyDispatchMethods; false otherwise. bool LegacyDispatchedSelector(Selector Sel); /// FinishNonFragileABIModule - Write out global data structures at the end of /// processing a translation unit. void FinishNonFragileABIModule(); /// AddModuleClassList - Add the given list of class pointers to the /// module with the provided symbol and section names. void AddModuleClassList(const std::vector &Container, const char *SymbolName, const char *SectionName); llvm::GlobalVariable * BuildClassRoTInitializer(unsigned flags, unsigned InstanceStart, unsigned InstanceSize, const ObjCImplementationDecl *ID); llvm::GlobalVariable * BuildClassMetaData(std::string &ClassName, llvm::Constant *IsAGV, llvm::Constant *SuperClassGV, llvm::Constant *ClassRoGV, bool HiddenVisibility); llvm::Constant *GetMethodConstant(const ObjCMethodDecl *MD); llvm::Constant *GetMethodDescriptionConstant(const ObjCMethodDecl *MD); /// EmitMethodList - Emit the method list for the given /// implementation. The return value has type MethodListnfABITy. llvm::Constant *EmitMethodList(llvm::Twine Name, const char *Section, const ConstantVector &Methods); /// EmitIvarList - Emit the ivar list for the given /// implementation. If ForClass is true the list of class ivars /// (i.e. metaclass ivars) is emitted, otherwise the list of /// interface ivars will be emitted. The return value has type /// IvarListnfABIPtrTy. llvm::Constant *EmitIvarList(const ObjCImplementationDecl *ID); llvm::Constant *EmitIvarOffsetVar(const ObjCInterfaceDecl *ID, const ObjCIvarDecl *Ivar, unsigned long int offset); /// GetOrEmitProtocol - Get the protocol object for the given /// declaration, emitting it if necessary. The return value has type /// ProtocolPtrTy. virtual llvm::Constant *GetOrEmitProtocol(const ObjCProtocolDecl *PD); /// GetOrEmitProtocolRef - Get a forward reference to the protocol /// object for the given declaration, emitting it if needed. These /// forward references will be filled in with empty bodies if no /// definition is seen. The return value has type ProtocolPtrTy. virtual llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD); /// EmitProtocolList - Generate the list of referenced /// protocols. The return value has type ProtocolListPtrTy. llvm::Constant *EmitProtocolList(llvm::Twine Name, ObjCProtocolDecl::protocol_iterator begin, ObjCProtocolDecl::protocol_iterator end); CodeGen::RValue EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Receiver, QualType Arg0Ty, bool IsSuper, const CallArgList &CallArgs); /// GetClassGlobal - Return the global variable for the Objective-C /// class of the given name. llvm::GlobalVariable *GetClassGlobal(const std::string &Name); /// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given class reference. llvm::Value *EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); /// EmitSuperClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given super class reference. llvm::Value *EmitSuperClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); /// EmitMetaClassRef - Return a Value * of the address of _class_t /// meta-data llvm::Value *EmitMetaClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); /// ObjCIvarOffsetVariable - Returns the ivar offset variable for /// the given ivar. /// llvm::GlobalVariable * ObjCIvarOffsetVariable( const ObjCInterfaceDecl *ID, const ObjCIvarDecl *Ivar); /// EmitSelector - Return a Value*, of type ObjCTypes.SelectorPtrTy, /// for the given selector. llvm::Value *EmitSelector(CGBuilderTy &Builder, Selector Sel); /// GetInterfaceEHType - Get the cached ehtype for the given Objective-C /// interface. The return value has type EHTypePtrTy. llvm::Value *GetInterfaceEHType(const ObjCInterfaceDecl *ID, bool ForDefinition); const char *getMetaclassSymbolPrefix() const { return "OBJC_METACLASS_$_"; } const char *getClassSymbolPrefix() const { return "OBJC_CLASS_$_"; } void GetClassSizeInfo(const ObjCImplementationDecl *OID, uint32_t &InstanceStart, uint32_t &InstanceSize); // Shamelessly stolen from Analysis/CFRefCount.cpp Selector GetNullarySelector(const char* name) const { IdentifierInfo* II = &CGM.getContext().Idents.get(name); return CGM.getContext().Selectors.getSelector(0, &II); } Selector GetUnarySelector(const char* name) const { IdentifierInfo* II = &CGM.getContext().Idents.get(name); return CGM.getContext().Selectors.getSelector(1, &II); } /// ImplementationIsNonLazy - Check whether the given category or /// class implementation is "non-lazy". bool ImplementationIsNonLazy(const ObjCImplDecl *OD) const; public: CGObjCNonFragileABIMac(CodeGen::CodeGenModule &cgm); // FIXME. All stubs for now! virtual llvm::Function *ModuleInitFunction(); virtual CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Receiver, const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method); virtual CodeGen::RValue GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, const ObjCInterfaceDecl *Class, bool isCategoryImpl, llvm::Value *Receiver, bool IsClassMessage, const CallArgList &CallArgs, const ObjCMethodDecl *Method); virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); virtual llvm::Value *GetSelector(CGBuilderTy &Builder, Selector Sel) { return EmitSelector(Builder, Sel); } /// The NeXT/Apple runtimes do not support typed selectors; just emit an /// untyped one. virtual llvm::Value *GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method) { return EmitSelector(Builder, Method->getSelector()); } virtual void GenerateCategory(const ObjCCategoryImplDecl *CMD); virtual void GenerateClass(const ObjCImplementationDecl *ClassDecl); virtual llvm::Value *GenerateProtocolRef(CGBuilderTy &Builder, const ObjCProtocolDecl *PD); virtual llvm::Constant *GetPropertyGetFunction() { return ObjCTypes.getGetPropertyFn(); } virtual llvm::Constant *GetPropertySetFunction() { return ObjCTypes.getSetPropertyFn(); } virtual llvm::Constant *GetCopyStructFunction() { return ObjCTypes.getCopyStructFn(); } virtual llvm::Constant *EnumerationMutationFunction() { return ObjCTypes.getEnumerationMutationFn(); } virtual void EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S); virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S); virtual llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, llvm::Value *AddrWeakObj); virtual void EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst); virtual void EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dest); virtual void EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dest, llvm::Value *ivarOffset); virtual void EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dest); virtual void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *dest, llvm::Value *src, QualType Ty); virtual LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy, llvm::Value *BaseValue, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers); virtual llvm::Value *EmitIvarOffset(CodeGen::CodeGenFunction &CGF, const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); }; } // end anonymous namespace /* *** Helper Functions *** */ /// getConstantGEP() - Help routine to construct simple GEPs. static llvm::Constant *getConstantGEP(llvm::LLVMContext &VMContext, llvm::Constant *C, unsigned idx0, unsigned idx1) { llvm::Value *Idxs[] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), idx0), llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), idx1) }; return llvm::ConstantExpr::getGetElementPtr(C, Idxs, 2); } /// hasObjCExceptionAttribute - Return true if this class or any super /// class has the __objc_exception__ attribute. static bool hasObjCExceptionAttribute(ASTContext &Context, const ObjCInterfaceDecl *OID) { if (OID->hasAttr()) return true; if (const ObjCInterfaceDecl *Super = OID->getSuperClass()) return hasObjCExceptionAttribute(Context, Super); return false; } /* *** CGObjCMac Public Interface *** */ CGObjCMac::CGObjCMac(CodeGen::CodeGenModule &cgm) : CGObjCCommonMac(cgm), ObjCTypes(cgm) { ObjCABI = 1; EmitImageInfo(); } /// GetClass - Return a reference to the class for the given interface /// decl. llvm::Value *CGObjCMac::GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { return EmitClassRef(Builder, ID); } /// GetSelector - Return the pointer to the unique'd string for this selector. llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, Selector Sel) { return EmitSelector(Builder, Sel); } llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method) { return EmitSelector(Builder, Method->getSelector()); } /// Generate a constant CFString object. /* struct __builtin_CFString { const int *isa; // point to __CFConstantStringClassReference int flags; const char *str; long length; }; */ /// or Generate a constant NSString object. /* struct __builtin_NSString { const int *isa; // point to __NSConstantStringClassReference const char *str; unsigned int length; }; */ llvm::Constant *CGObjCCommonMac::GenerateConstantString( const StringLiteral *SL) { return (CGM.getLangOptions().NoConstantCFStrings == 0 ? CGM.GetAddrOfConstantCFString(SL) : CGM.GetAddrOfConstantNSString(SL)); } /// 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. CodeGen::RValue CGObjCMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, const ObjCInterfaceDecl *Class, bool isCategoryImpl, llvm::Value *Receiver, bool IsClassMessage, const CodeGen::CallArgList &CallArgs, const ObjCMethodDecl *Method) { // Create and init a super structure; this is a (receiver, class) // pair we will pass to objc_msgSendSuper. llvm::Value *ObjCSuper = CGF.Builder.CreateAlloca(ObjCTypes.SuperTy, 0, "objc_super"); llvm::Value *ReceiverAsObject = CGF.Builder.CreateBitCast(Receiver, ObjCTypes.ObjectPtrTy); CGF.Builder.CreateStore(ReceiverAsObject, CGF.Builder.CreateStructGEP(ObjCSuper, 0)); // If this is a class message the metaclass is passed as the target. llvm::Value *Target; if (IsClassMessage) { if (isCategoryImpl) { // Message sent to 'super' in a class method defined in a category // implementation requires an odd treatment. // If we are in a class method, we must retrieve the // _metaclass_ for the current class, pointed at by // the class's "isa" pointer. The following assumes that // isa" is the first ivar in a class (which it must be). Target = EmitClassRef(CGF.Builder, Class->getSuperClass()); Target = CGF.Builder.CreateStructGEP(Target, 0); Target = CGF.Builder.CreateLoad(Target); } else { llvm::Value *MetaClassPtr = EmitMetaClassRef(Class); llvm::Value *SuperPtr = CGF.Builder.CreateStructGEP(MetaClassPtr, 1); llvm::Value *Super = CGF.Builder.CreateLoad(SuperPtr); Target = Super; } } else if (isCategoryImpl) Target = EmitClassRef(CGF.Builder, Class->getSuperClass()); else { llvm::Value *ClassPtr = EmitSuperClassRef(Class); ClassPtr = CGF.Builder.CreateStructGEP(ClassPtr, 1); Target = CGF.Builder.CreateLoad(ClassPtr); } // FIXME: We shouldn't need to do this cast, rectify the ASTContext and // ObjCTypes types. const llvm::Type *ClassTy = CGM.getTypes().ConvertType(CGF.getContext().getObjCClassType()); Target = CGF.Builder.CreateBitCast(Target, ClassTy); CGF.Builder.CreateStore(Target, CGF.Builder.CreateStructGEP(ObjCSuper, 1)); return EmitLegacyMessageSend(CGF, Return, ResultType, EmitSelector(CGF.Builder, Sel), ObjCSuper, ObjCTypes.SuperPtrCTy, true, CallArgs, Method, ObjCTypes); } /// Generate code for a message send expression. CodeGen::RValue CGObjCMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Receiver, const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method) { return EmitLegacyMessageSend(CGF, Return, ResultType, EmitSelector(CGF.Builder, Sel), Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method, ObjCTypes); } CodeGen::RValue CGObjCCommonMac::EmitLegacyMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, llvm::Value *Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, const CallArgList &CallArgs, const ObjCMethodDecl *Method, const ObjCCommonTypesHelper &ObjCTypes) { CallArgList ActualArgs; if (!IsSuper) Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy, "tmp"); ActualArgs.push_back(std::make_pair(RValue::get(Arg0), Arg0Ty)); ActualArgs.push_back(std::make_pair(RValue::get(Sel), CGF.getContext().getObjCSelType())); ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, FunctionType::ExtInfo()); const llvm::FunctionType *FTy = Types.GetFunctionType(FnInfo, Method ? Method->isVariadic() : false); llvm::Constant *Fn = NULL; if (CGM.ReturnTypeUsesSret(FnInfo)) { Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper) : ObjCTypes.getSendStretFn(IsSuper); } else if (ResultType->isFloatingType()) { if (ObjCABI == 2) { if (const BuiltinType *BT = ResultType->getAs()) { BuiltinType::Kind k = BT->getKind(); Fn = (k == BuiltinType::LongDouble) ? ObjCTypes.getSendFpretFn2(IsSuper) : ObjCTypes.getSendFn2(IsSuper); } else { Fn = ObjCTypes.getSendFn2(IsSuper); } } else // FIXME. This currently matches gcc's API for x86-32. May need to change // for others if we have their API. Fn = ObjCTypes.getSendFpretFn(IsSuper); } else { Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper) : ObjCTypes.getSendFn(IsSuper); } assert(Fn && "EmitLegacyMessageSend - unknown API"); Fn = llvm::ConstantExpr::getBitCast(Fn, llvm::PointerType::getUnqual(FTy)); return CGF.EmitCall(FnInfo, Fn, Return, ActualArgs); } llvm::Value *CGObjCMac::GenerateProtocolRef(CGBuilderTy &Builder, const ObjCProtocolDecl *PD) { // FIXME: I don't understand why gcc generates this, or where it is // resolved. Investigate. Its also wasteful to look this up over and over. LazySymbols.insert(&CGM.getContext().Idents.get("Protocol")); return llvm::ConstantExpr::getBitCast(GetProtocolRef(PD), ObjCTypes.ExternalProtocolPtrTy); } void CGObjCCommonMac::GenerateProtocol(const ObjCProtocolDecl *PD) { // FIXME: We shouldn't need this, the protocol decl should contain enough // information to tell us whether this was a declaration or a definition. DefinedProtocols.insert(PD->getIdentifier()); // If we have generated a forward reference to this protocol, emit // it now. Otherwise do nothing, the protocol objects are lazily // emitted. if (Protocols.count(PD->getIdentifier())) GetOrEmitProtocol(PD); } llvm::Constant *CGObjCCommonMac::GetProtocolRef(const ObjCProtocolDecl *PD) { if (DefinedProtocols.count(PD->getIdentifier())) return GetOrEmitProtocol(PD); return GetOrEmitProtocolRef(PD); } /* // APPLE LOCAL radar 4585769 - Objective-C 1.0 extensions struct _objc_protocol { struct _objc_protocol_extension *isa; char *protocol_name; struct _objc_protocol_list *protocol_list; struct _objc__method_prototype_list *instance_methods; struct _objc__method_prototype_list *class_methods }; See EmitProtocolExtension(). */ llvm::Constant *CGObjCMac::GetOrEmitProtocol(const ObjCProtocolDecl *PD) { llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()]; // Early exit if a defining object has already been generated. if (Entry && Entry->hasInitializer()) return Entry; // FIXME: I don't understand why gcc generates this, or where it is // resolved. Investigate. Its also wasteful to look this up over and over. LazySymbols.insert(&CGM.getContext().Idents.get("Protocol")); // Construct method lists. std::vector InstanceMethods, ClassMethods; std::vector OptInstanceMethods, OptClassMethods; for (ObjCProtocolDecl::instmeth_iterator i = PD->instmeth_begin(), e = PD->instmeth_end(); i != e; ++i) { ObjCMethodDecl *MD = *i; llvm::Constant *C = GetMethodDescriptionConstant(MD); if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { OptInstanceMethods.push_back(C); } else { InstanceMethods.push_back(C); } } for (ObjCProtocolDecl::classmeth_iterator i = PD->classmeth_begin(), e = PD->classmeth_end(); i != e; ++i) { ObjCMethodDecl *MD = *i; llvm::Constant *C = GetMethodDescriptionConstant(MD); if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { OptClassMethods.push_back(C); } else { ClassMethods.push_back(C); } } std::vector Values(5); Values[0] = EmitProtocolExtension(PD, OptInstanceMethods, OptClassMethods); Values[1] = GetClassName(PD->getIdentifier()); Values[2] = EmitProtocolList("\01L_OBJC_PROTOCOL_REFS_" + PD->getName(), PD->protocol_begin(), PD->protocol_end()); Values[3] = EmitMethodDescList("\01L_OBJC_PROTOCOL_INSTANCE_METHODS_" + PD->getName(), "__OBJC,__cat_inst_meth,regular,no_dead_strip", InstanceMethods); Values[4] = EmitMethodDescList("\01L_OBJC_PROTOCOL_CLASS_METHODS_" + PD->getName(), "__OBJC,__cat_cls_meth,regular,no_dead_strip", ClassMethods); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ProtocolTy, Values); if (Entry) { // Already created, fix the linkage and update the initializer. Entry->setLinkage(llvm::GlobalValue::InternalLinkage); Entry->setInitializer(Init); } else { Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolTy, false, llvm::GlobalValue::InternalLinkage, Init, "\01L_OBJC_PROTOCOL_" + PD->getName()); Entry->setSection("__OBJC,__protocol,regular,no_dead_strip"); // FIXME: Is this necessary? Why only for protocol? Entry->setAlignment(4); } CGM.AddUsedGlobal(Entry); return Entry; } llvm::Constant *CGObjCMac::GetOrEmitProtocolRef(const ObjCProtocolDecl *PD) { llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()]; if (!Entry) { // We use the initializer as a marker of whether this is a forward // reference or not. At module finalization we add the empty // contents for protocols which were referenced but never defined. Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolTy, false, llvm::GlobalValue::ExternalLinkage, 0, "\01L_OBJC_PROTOCOL_" + PD->getName()); Entry->setSection("__OBJC,__protocol,regular,no_dead_strip"); // FIXME: Is this necessary? Why only for protocol? Entry->setAlignment(4); } return Entry; } /* struct _objc_protocol_extension { uint32_t size; struct objc_method_description_list *optional_instance_methods; struct objc_method_description_list *optional_class_methods; struct objc_property_list *instance_properties; }; */ llvm::Constant * CGObjCMac::EmitProtocolExtension(const ObjCProtocolDecl *PD, const ConstantVector &OptInstanceMethods, const ConstantVector &OptClassMethods) { uint64_t Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ProtocolExtensionTy); std::vector Values(4); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Values[1] = EmitMethodDescList("\01L_OBJC_PROTOCOL_INSTANCE_METHODS_OPT_" + PD->getName(), "__OBJC,__cat_inst_meth,regular,no_dead_strip", OptInstanceMethods); Values[2] = EmitMethodDescList("\01L_OBJC_PROTOCOL_CLASS_METHODS_OPT_" + PD->getName(), "__OBJC,__cat_cls_meth,regular,no_dead_strip", OptClassMethods); Values[3] = EmitPropertyList("\01L_OBJC_$_PROP_PROTO_LIST_" + PD->getName(), 0, PD, ObjCTypes); // Return null if no extension bits are used. if (Values[1]->isNullValue() && Values[2]->isNullValue() && Values[3]->isNullValue()) return llvm::Constant::getNullValue(ObjCTypes.ProtocolExtensionPtrTy); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ProtocolExtensionTy, Values); // No special section, but goes in llvm.used return CreateMetadataVar("\01L_OBJC_PROTOCOLEXT_" + PD->getName(), Init, 0, 0, true); } /* struct objc_protocol_list { struct objc_protocol_list *next; long count; Protocol *list[]; }; */ llvm::Constant * CGObjCMac::EmitProtocolList(llvm::Twine Name, ObjCProtocolDecl::protocol_iterator begin, ObjCProtocolDecl::protocol_iterator end) { std::vector ProtocolRefs; for (; begin != end; ++begin) ProtocolRefs.push_back(GetProtocolRef(*begin)); // Just return null for empty protocol lists if (ProtocolRefs.empty()) return llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy); // This list is null terminated. ProtocolRefs.push_back(llvm::Constant::getNullValue(ObjCTypes.ProtocolPtrTy)); std::vector Values(3); // This field is only used by the runtime. Values[0] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy); Values[1] = llvm::ConstantInt::get(ObjCTypes.LongTy, ProtocolRefs.size() - 1); Values[2] = llvm::ConstantArray::get(llvm::ArrayType::get(ObjCTypes.ProtocolPtrTy, ProtocolRefs.size()), ProtocolRefs); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, "__OBJC,__cat_cls_meth,regular,no_dead_strip", 4, false); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ProtocolListPtrTy); } void CGObjCCommonMac::PushProtocolProperties(llvm::SmallPtrSet &PropertySet, std::vector &Properties, const Decl *Container, const ObjCProtocolDecl *PROTO, const ObjCCommonTypesHelper &ObjCTypes) { std::vector Prop(2); for (ObjCProtocolDecl::protocol_iterator P = PROTO->protocol_begin(), E = PROTO->protocol_end(); P != E; ++P) PushProtocolProperties(PropertySet, Properties, Container, (*P), ObjCTypes); for (ObjCContainerDecl::prop_iterator I = PROTO->prop_begin(), E = PROTO->prop_end(); I != E; ++I) { const ObjCPropertyDecl *PD = *I; if (!PropertySet.insert(PD->getIdentifier())) continue; Prop[0] = GetPropertyName(PD->getIdentifier()); Prop[1] = GetPropertyTypeString(PD, Container); Properties.push_back(llvm::ConstantStruct::get(ObjCTypes.PropertyTy, Prop)); } } /* struct _objc_property { const char * const name; const char * const attributes; }; struct _objc_property_list { uint32_t entsize; // sizeof (struct _objc_property) uint32_t prop_count; struct _objc_property[prop_count]; }; */ llvm::Constant *CGObjCCommonMac::EmitPropertyList(llvm::Twine Name, const Decl *Container, const ObjCContainerDecl *OCD, const ObjCCommonTypesHelper &ObjCTypes) { std::vector Properties, Prop(2); llvm::SmallPtrSet PropertySet; for (ObjCContainerDecl::prop_iterator I = OCD->prop_begin(), E = OCD->prop_end(); I != E; ++I) { const ObjCPropertyDecl *PD = *I; PropertySet.insert(PD->getIdentifier()); Prop[0] = GetPropertyName(PD->getIdentifier()); Prop[1] = GetPropertyTypeString(PD, Container); Properties.push_back(llvm::ConstantStruct::get(ObjCTypes.PropertyTy, Prop)); } if (const ObjCInterfaceDecl *OID = dyn_cast(OCD)) for (ObjCInterfaceDecl::protocol_iterator P = OID->protocol_begin(), E = OID->protocol_end(); P != E; ++P) PushProtocolProperties(PropertySet, Properties, Container, (*P), ObjCTypes); // Return null for empty list. if (Properties.empty()) return llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); unsigned PropertySize = CGM.getTargetData().getTypeAllocSize(ObjCTypes.PropertyTy); std::vector Values(3); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, PropertySize); Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Properties.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.PropertyTy, Properties.size()); Values[2] = llvm::ConstantArray::get(AT, Properties); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, (ObjCABI == 2) ? "__DATA, __objc_const" : "__OBJC,__property,regular,no_dead_strip", (ObjCABI == 2) ? 8 : 4, true); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.PropertyListPtrTy); } /* struct objc_method_description_list { int count; struct objc_method_description list[]; }; */ llvm::Constant * CGObjCMac::GetMethodDescriptionConstant(const ObjCMethodDecl *MD) { std::vector Desc(2); Desc[0] = llvm::ConstantExpr::getBitCast(GetMethodVarName(MD->getSelector()), ObjCTypes.SelectorPtrTy); Desc[1] = GetMethodVarType(MD); return llvm::ConstantStruct::get(ObjCTypes.MethodDescriptionTy, Desc); } llvm::Constant *CGObjCMac::EmitMethodDescList(llvm::Twine Name, const char *Section, const ConstantVector &Methods) { // Return null for empty list. if (Methods.empty()) return llvm::Constant::getNullValue(ObjCTypes.MethodDescriptionListPtrTy); std::vector Values(2); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Methods.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.MethodDescriptionTy, Methods.size()); Values[1] = llvm::ConstantArray::get(AT, Methods); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, Section, 4, true); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.MethodDescriptionListPtrTy); } /* struct _objc_category { char *category_name; char *class_name; struct _objc_method_list *instance_methods; struct _objc_method_list *class_methods; struct _objc_protocol_list *protocols; uint32_t size; // struct _objc_property_list *instance_properties; }; */ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { unsigned Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.CategoryTy); // FIXME: This is poor design, the OCD should have a pointer to the category // decl. Additionally, note that Category can be null for the @implementation // w/o an @interface case. Sema should just create one for us as it does for // @implementation so everyone else can live life under a clear blue sky. const ObjCInterfaceDecl *Interface = OCD->getClassInterface(); const ObjCCategoryDecl *Category = Interface->FindCategoryDeclaration(OCD->getIdentifier()); llvm::SmallString<256> ExtName; llvm::raw_svector_ostream(ExtName) << Interface->getName() << '_' << OCD->getName(); std::vector InstanceMethods, ClassMethods; for (ObjCCategoryImplDecl::instmeth_iterator i = OCD->instmeth_begin(), e = OCD->instmeth_end(); i != e; ++i) { // Instance methods should always be defined. InstanceMethods.push_back(GetMethodConstant(*i)); } for (ObjCCategoryImplDecl::classmeth_iterator i = OCD->classmeth_begin(), e = OCD->classmeth_end(); i != e; ++i) { // Class methods should always be defined. ClassMethods.push_back(GetMethodConstant(*i)); } std::vector Values(7); Values[0] = GetClassName(OCD->getIdentifier()); Values[1] = GetClassName(Interface->getIdentifier()); LazySymbols.insert(Interface->getIdentifier()); Values[2] = EmitMethodList("\01L_OBJC_CATEGORY_INSTANCE_METHODS_" + ExtName.str(), "__OBJC,__cat_inst_meth,regular,no_dead_strip", InstanceMethods); Values[3] = EmitMethodList("\01L_OBJC_CATEGORY_CLASS_METHODS_" + ExtName.str(), "__OBJC,__cat_cls_meth,regular,no_dead_strip", ClassMethods); if (Category) { Values[4] = EmitProtocolList("\01L_OBJC_CATEGORY_PROTOCOLS_" + ExtName.str(), Category->protocol_begin(), Category->protocol_end()); } else { Values[4] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy); } Values[5] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); // If there is no category @interface then there can be no properties. if (Category) { Values[6] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ExtName.str(), OCD, Category, ObjCTypes); } else { Values[6] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); } llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.CategoryTy, Values); llvm::GlobalVariable *GV = CreateMetadataVar("\01L_OBJC_CATEGORY_" + ExtName.str(), Init, "__OBJC,__category,regular,no_dead_strip", 4, true); DefinedCategories.push_back(GV); } // FIXME: Get from somewhere? enum ClassFlags { eClassFlags_Factory = 0x00001, eClassFlags_Meta = 0x00002, // eClassFlags_HasCXXStructors = 0x02000, eClassFlags_Hidden = 0x20000, eClassFlags_ABI2_Hidden = 0x00010, eClassFlags_ABI2_HasCXXStructors = 0x00004 // }; /* struct _objc_class { Class isa; Class super_class; const char *name; long version; long info; long instance_size; struct _objc_ivar_list *ivars; struct _objc_method_list *methods; struct _objc_cache *cache; struct _objc_protocol_list *protocols; // Objective-C 1.0 extensions () const char *ivar_layout; struct _objc_class_ext *ext; }; See EmitClassExtension(); */ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) { DefinedSymbols.insert(ID->getIdentifier()); std::string ClassName = ID->getNameAsString(); // FIXME: Gross ObjCInterfaceDecl *Interface = const_cast(ID->getClassInterface()); llvm::Constant *Protocols = EmitProtocolList("\01L_OBJC_CLASS_PROTOCOLS_" + ID->getName(), Interface->protocol_begin(), Interface->protocol_end()); unsigned Flags = eClassFlags_Factory; if (ID->getNumIvarInitializers()) Flags |= eClassFlags_HasCXXStructors; unsigned Size = CGM.getContext().getASTObjCImplementationLayout(ID).getSize() / 8; // FIXME: Set CXX-structors flag. if (CGM.getDeclVisibilityMode(ID->getClassInterface()) == LangOptions::Hidden) Flags |= eClassFlags_Hidden; std::vector InstanceMethods, ClassMethods; for (ObjCImplementationDecl::instmeth_iterator i = ID->instmeth_begin(), e = ID->instmeth_end(); i != e; ++i) { // Instance methods should always be defined. InstanceMethods.push_back(GetMethodConstant(*i)); } for (ObjCImplementationDecl::classmeth_iterator i = ID->classmeth_begin(), e = ID->classmeth_end(); i != e; ++i) { // Class methods should always be defined. ClassMethods.push_back(GetMethodConstant(*i)); } for (ObjCImplementationDecl::propimpl_iterator i = ID->propimpl_begin(), e = ID->propimpl_end(); i != e; ++i) { ObjCPropertyImplDecl *PID = *i; if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) { ObjCPropertyDecl *PD = PID->getPropertyDecl(); if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) if (llvm::Constant *C = GetMethodConstant(MD)) InstanceMethods.push_back(C); if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) if (llvm::Constant *C = GetMethodConstant(MD)) InstanceMethods.push_back(C); } } std::vector Values(12); Values[ 0] = EmitMetaClass(ID, Protocols, ClassMethods); if (ObjCInterfaceDecl *Super = Interface->getSuperClass()) { // Record a reference to the super class. LazySymbols.insert(Super->getIdentifier()); Values[ 1] = llvm::ConstantExpr::getBitCast(GetClassName(Super->getIdentifier()), ObjCTypes.ClassPtrTy); } else { Values[ 1] = llvm::Constant::getNullValue(ObjCTypes.ClassPtrTy); } Values[ 2] = GetClassName(ID->getIdentifier()); // Version is always 0. Values[ 3] = llvm::ConstantInt::get(ObjCTypes.LongTy, 0); Values[ 4] = llvm::ConstantInt::get(ObjCTypes.LongTy, Flags); Values[ 5] = llvm::ConstantInt::get(ObjCTypes.LongTy, Size); Values[ 6] = EmitIvarList(ID, false); Values[ 7] = EmitMethodList("\01L_OBJC_INSTANCE_METHODS_" + ID->getName(), "__OBJC,__inst_meth,regular,no_dead_strip", InstanceMethods); // cache is always NULL. Values[ 8] = llvm::Constant::getNullValue(ObjCTypes.CachePtrTy); Values[ 9] = Protocols; Values[10] = BuildIvarLayout(ID, true); Values[11] = EmitClassExtension(ID); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassTy, Values); std::string Name("\01L_OBJC_CLASS_"); Name += ClassName; const char *Section = "__OBJC,__class,regular,no_dead_strip"; // 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); GV->setSection(Section); GV->setAlignment(4); CGM.AddUsedGlobal(GV); } else GV = CreateMetadataVar(Name, Init, Section, 4, true); DefinedClasses.push_back(GV); } llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID, llvm::Constant *Protocols, const ConstantVector &Methods) { unsigned Flags = eClassFlags_Meta; unsigned Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ClassTy); if (CGM.getDeclVisibilityMode(ID->getClassInterface()) == LangOptions::Hidden) Flags |= eClassFlags_Hidden; std::vector Values(12); // The isa for the metaclass is the root of the hierarchy. const ObjCInterfaceDecl *Root = ID->getClassInterface(); while (const ObjCInterfaceDecl *Super = Root->getSuperClass()) Root = Super; Values[ 0] = llvm::ConstantExpr::getBitCast(GetClassName(Root->getIdentifier()), ObjCTypes.ClassPtrTy); // The super class for the metaclass is emitted as the name of the // super class. The runtime fixes this up to point to the // *metaclass* for the super class. if (ObjCInterfaceDecl *Super = ID->getClassInterface()->getSuperClass()) { Values[ 1] = llvm::ConstantExpr::getBitCast(GetClassName(Super->getIdentifier()), ObjCTypes.ClassPtrTy); } else { Values[ 1] = llvm::Constant::getNullValue(ObjCTypes.ClassPtrTy); } Values[ 2] = GetClassName(ID->getIdentifier()); // Version is always 0. Values[ 3] = llvm::ConstantInt::get(ObjCTypes.LongTy, 0); Values[ 4] = llvm::ConstantInt::get(ObjCTypes.LongTy, Flags); Values[ 5] = llvm::ConstantInt::get(ObjCTypes.LongTy, Size); Values[ 6] = EmitIvarList(ID, true); Values[ 7] = EmitMethodList("\01L_OBJC_CLASS_METHODS_" + ID->getNameAsString(), "__OBJC,__cls_meth,regular,no_dead_strip", Methods); // cache is always NULL. Values[ 8] = llvm::Constant::getNullValue(ObjCTypes.CachePtrTy); Values[ 9] = Protocols; // ivar_layout for metaclass is always NULL. Values[10] = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); // The class extension is always unused for metaclasses. Values[11] = llvm::Constant::getNullValue(ObjCTypes.ClassExtensionPtrTy); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassTy, Values); std::string Name("\01L_OBJC_METACLASS_"); Name += ID->getNameAsCString(); // 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(CGM.getModule(), ObjCTypes.ClassTy, false, llvm::GlobalValue::InternalLinkage, Init, Name); } GV->setSection("__OBJC,__meta_class,regular,no_dead_strip"); GV->setAlignment(4); CGM.AddUsedGlobal(GV); return GV; } llvm::Constant *CGObjCMac::EmitMetaClassRef(const ObjCInterfaceDecl *ID) { std::string Name = "\01L_OBJC_METACLASS_" + ID->getNameAsString(); // 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. // Previously, metaclass with internal linkage may have been defined. // pass 'true' as 2nd argument so it is returned. if (llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name, true)) { 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(CGM.getModule(), ObjCTypes.ClassTy, false, llvm::GlobalValue::ExternalLinkage, 0, Name); } } llvm::Value *CGObjCMac::EmitSuperClassRef(const ObjCInterfaceDecl *ID) { std::string Name = "\01L_OBJC_CLASS_" + ID->getNameAsString(); if (llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name, true)) { assert(GV->getType()->getElementType() == ObjCTypes.ClassTy && "Forward class metadata reference has incorrect type."); return GV; } else { return new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassTy, false, llvm::GlobalValue::ExternalLinkage, 0, Name); } } /* struct objc_class_ext { uint32_t size; const char *weak_ivar_layout; struct _objc_property_list *properties; }; */ llvm::Constant * CGObjCMac::EmitClassExtension(const ObjCImplementationDecl *ID) { uint64_t Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ClassExtensionTy); std::vector Values(3); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Values[1] = BuildIvarLayout(ID, false); Values[2] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ID->getName(), ID, ID->getClassInterface(), ObjCTypes); // Return null if no extension bits are used. if (Values[1]->isNullValue() && Values[2]->isNullValue()) return llvm::Constant::getNullValue(ObjCTypes.ClassExtensionPtrTy); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassExtensionTy, Values); return CreateMetadataVar("\01L_OBJC_CLASSEXT_" + ID->getName(), Init, "__OBJC,__class_ext,regular,no_dead_strip", 4, true); } /* struct objc_ivar { char *ivar_name; char *ivar_type; int ivar_offset; }; struct objc_ivar_list { int ivar_count; struct objc_ivar list[count]; }; */ llvm::Constant *CGObjCMac::EmitIvarList(const ObjCImplementationDecl *ID, bool ForClass) { std::vector Ivars, Ivar(3); // When emitting the root class GCC emits ivar entries for the // actual class structure. It is not clear if we need to follow this // behavior; for now lets try and get away with not doing it. If so, // the cleanest solution would be to make up an ObjCInterfaceDecl // for the class. if (ForClass) return llvm::Constant::getNullValue(ObjCTypes.IvarListPtrTy); ObjCInterfaceDecl *OID = const_cast(ID->getClassInterface()); llvm::SmallVector OIvars; CGM.getContext().ShallowCollectObjCIvars(OID, OIvars); for (unsigned i = 0, e = OIvars.size(); i != e; ++i) { ObjCIvarDecl *IVD = OIvars[i]; // Ignore unnamed bit-fields. if (!IVD->getDeclName()) continue; Ivar[0] = GetMethodVarName(IVD->getIdentifier()); Ivar[1] = GetMethodVarType(IVD); Ivar[2] = llvm::ConstantInt::get(ObjCTypes.IntTy, ComputeIvarBaseOffset(CGM, OID, IVD)); Ivars.push_back(llvm::ConstantStruct::get(ObjCTypes.IvarTy, Ivar)); } // Return null for empty list. if (Ivars.empty()) return llvm::Constant::getNullValue(ObjCTypes.IvarListPtrTy); std::vector Values(2); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Ivars.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.IvarTy, Ivars.size()); Values[1] = llvm::ConstantArray::get(AT, Ivars); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); llvm::GlobalVariable *GV; if (ForClass) GV = CreateMetadataVar("\01L_OBJC_CLASS_VARIABLES_" + ID->getName(), Init, "__OBJC,__class_vars,regular,no_dead_strip", 4, true); else GV = CreateMetadataVar("\01L_OBJC_INSTANCE_VARIABLES_" + ID->getName(), Init, "__OBJC,__instance_vars,regular,no_dead_strip", 4, true); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.IvarListPtrTy); } /* struct objc_method { SEL method_name; char *method_types; void *method; }; struct objc_method_list { struct objc_method_list *obsolete; int count; struct objc_method methods_list[count]; }; */ /// GetMethodConstant - Return a struct objc_method constant for the /// given method if it has been defined. The result is null if the /// method has not been defined. The return value has type MethodPtrTy. llvm::Constant *CGObjCMac::GetMethodConstant(const ObjCMethodDecl *MD) { // FIXME: Use DenseMap::lookup llvm::Function *Fn = MethodDefinitions[MD]; if (!Fn) return 0; std::vector Method(3); Method[0] = llvm::ConstantExpr::getBitCast(GetMethodVarName(MD->getSelector()), ObjCTypes.SelectorPtrTy); Method[1] = GetMethodVarType(MD); Method[2] = llvm::ConstantExpr::getBitCast(Fn, ObjCTypes.Int8PtrTy); return llvm::ConstantStruct::get(ObjCTypes.MethodTy, Method); } llvm::Constant *CGObjCMac::EmitMethodList(llvm::Twine Name, const char *Section, const ConstantVector &Methods) { // Return null for empty list. if (Methods.empty()) return llvm::Constant::getNullValue(ObjCTypes.MethodListPtrTy); std::vector Values(3); Values[0] = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Methods.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.MethodTy, Methods.size()); Values[2] = llvm::ConstantArray::get(AT, Methods); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, Section, 4, true); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.MethodListPtrTy); } llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) { llvm::SmallString<256> Name; GetNameForMethod(OMD, CD, Name); CodeGenTypes &Types = CGM.getTypes(); const llvm::FunctionType *MethodTy = Types.GetFunctionType(Types.getFunctionInfo(OMD), OMD->isVariadic()); llvm::Function *Method = llvm::Function::Create(MethodTy, llvm::GlobalValue::InternalLinkage, Name.str(), &CGM.getModule()); MethodDefinitions.insert(std::make_pair(OMD, Method)); return Method; } llvm::GlobalVariable * CGObjCCommonMac::CreateMetadataVar(llvm::Twine Name, llvm::Constant *Init, const char *Section, unsigned Align, bool AddToUsed) { const llvm::Type *Ty = Init->getType(); llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), Ty, false, llvm::GlobalValue::InternalLinkage, Init, Name); if (Section) GV->setSection(Section); if (Align) GV->setAlignment(Align); if (AddToUsed) CGM.AddUsedGlobal(GV); return GV; } llvm::Function *CGObjCMac::ModuleInitFunction() { // Abuse this interface function as a place to finalize. FinishModule(); return NULL; } llvm::Constant *CGObjCMac::GetPropertyGetFunction() { return ObjCTypes.getGetPropertyFn(); } llvm::Constant *CGObjCMac::GetPropertySetFunction() { return ObjCTypes.getSetPropertyFn(); } llvm::Constant *CGObjCMac::GetCopyStructFunction() { return ObjCTypes.getCopyStructFn(); } llvm::Constant *CGObjCMac::EnumerationMutationFunction() { return ObjCTypes.getEnumerationMutationFn(); } /* Objective-C setjmp-longjmp (sjlj) Exception Handling -- The basic framework for a @try-catch-finally is as follows: { objc_exception_data d; id _rethrow = null; bool _call_try_exit = true; objc_exception_try_enter(&d); if (!setjmp(d.jmp_buf)) { ... try body ... } else { // exception path id _caught = objc_exception_extract(&d); // enter new try scope for handlers if (!setjmp(d.jmp_buf)) { ... match exception and execute catch blocks ... // fell off end, rethrow. _rethrow = _caught; ... jump-through-finally to finally_rethrow ... } else { // exception in catch block _rethrow = objc_exception_extract(&d); _call_try_exit = false; ... jump-through-finally to finally_rethrow ... } } ... jump-through-finally to finally_end ... finally: if (_call_try_exit) objc_exception_try_exit(&d); ... finally block .... ... dispatch to finally destination ... finally_rethrow: objc_exception_throw(_rethrow); finally_end: } This framework differs slightly from the one gcc uses, in that gcc uses _rethrow to determine if objc_exception_try_exit should be called and if the object should be rethrown. This breaks in the face of throwing nil and introduces unnecessary branches. We specialize this framework for a few particular circumstances: - If there are no catch blocks, then we avoid emitting the second exception handling context. - If there is a catch-all catch block (i.e. @catch(...) or @catch(id e)) we avoid emitting the code to rethrow an uncaught exception. - FIXME: If there is no @finally block we can do a few more simplifications. Rethrows and Jumps-Through-Finally -- Support for implicit rethrows and jumping through the finally block is handled by storing the current exception-handling context in ObjCEHStack. In order to implement proper @finally semantics, we support one basic mechanism for jumping through the finally block to an arbitrary destination. Constructs which generate exits from a @try or @catch block use this mechanism to implement the proper semantics by chaining jumps, as necessary. This mechanism works like the one used for indirect goto: we arbitrarily assign an ID to each destination and store the ID for the destination in a variable prior to entering the finally block. At the end of the finally block we simply create a switch to the proper destination. Code gen for @synchronized(expr) stmt; Effectively generating code for: objc_sync_enter(expr); @try stmt @finally { objc_sync_exit(expr); } */ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S) { bool isTry = isa(S); // Create various blocks we refer to for handling @finally. llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally"); llvm::BasicBlock *FinallyExit = CGF.createBasicBlock("finally.exit"); llvm::BasicBlock *FinallyNoExit = CGF.createBasicBlock("finally.noexit"); llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw"); llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end"); // For @synchronized, call objc_sync_enter(sync.expr). The // evaluation of the expression must occur before we enter the // @synchronized. We can safely avoid a temp here because jumps into // @synchronized are illegal & this will dominate uses. llvm::Value *SyncArg = 0; if (!isTry) { SyncArg = CGF.EmitScalarExpr(cast(S).getSynchExpr()); SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy); CGF.Builder.CreateCall(ObjCTypes.getSyncEnterFn(), SyncArg); } // Push an EH context entry, used for handling rethrows and jumps // through finally. CGF.PushCleanupBlock(FinallyBlock); if (CGF.ObjCEHValueStack.empty()) CGF.ObjCEHValueStack.push_back(0); // If This is a nested @try, caught exception is that of enclosing @try. else CGF.ObjCEHValueStack.push_back(CGF.ObjCEHValueStack.back()); // Allocate memory for the exception data and rethrow pointer. llvm::Value *ExceptionData = CGF.CreateTempAlloca(ObjCTypes.ExceptionDataTy, "exceptiondata.ptr"); llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy, "_rethrow"); llvm::Value *CallTryExitPtr = CGF.CreateTempAlloca( llvm::Type::getInt1Ty(VMContext), "_call_try_exit"); CGF.Builder.CreateStore(llvm::ConstantInt::getTrue(VMContext), CallTryExitPtr); // Enter a new try block and call setjmp. CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData); llvm::Value *JmpBufPtr = CGF.Builder.CreateStructGEP(ExceptionData, 0, "jmpbufarray"); JmpBufPtr = CGF.Builder.CreateStructGEP(JmpBufPtr, 0, "tmp"); llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.getSetJmpFn(), JmpBufPtr, "result"); llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try"); llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler"); CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNotNull(SetJmpResult, "threw"), TryHandler, TryBlock); // Emit the @try block. CGF.EmitBlock(TryBlock); CGF.EmitStmt(isTry ? cast(S).getTryBody() : cast(S).getSynchBody()); CGF.EmitBranchThroughCleanup(FinallyEnd); // Emit the "exception in @try" block. CGF.EmitBlock(TryHandler); // Retrieve the exception object. We may emit multiple blocks but // nothing can cross this so the value is already in SSA form. llvm::Value *Caught = CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(), ExceptionData, "caught"); CGF.ObjCEHValueStack.back() = Caught; if (!isTry) { CGF.Builder.CreateStore(Caught, RethrowPtr); CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), CallTryExitPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); } else if (cast(S).getNumCatchStmts()) { const ObjCAtTryStmt* AtTryStmt = cast(&S); // Enter a new exception try block (in case a @catch block throws // an exception). CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData); llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.getSetJmpFn(), JmpBufPtr, "result"); llvm::Value *Threw = CGF.Builder.CreateIsNotNull(SetJmpResult, "threw"); llvm::BasicBlock *CatchBlock = CGF.createBasicBlock("catch"); llvm::BasicBlock *CatchHandler = CGF.createBasicBlock("catch.handler"); CGF.Builder.CreateCondBr(Threw, CatchHandler, CatchBlock); CGF.EmitBlock(CatchBlock); // Handle catch list. As a special case we check if everything is // matched and avoid generating code for falling off the end if // so. bool AllMatched = false; for (unsigned I = 0, N = AtTryStmt->getNumCatchStmts(); I != N; ++I) { const ObjCAtCatchStmt *CatchStmt = AtTryStmt->getCatchStmt(I); llvm::BasicBlock *NextCatchBlock = CGF.createBasicBlock("catch"); const VarDecl *CatchParam = CatchStmt->getCatchParamDecl(); const ObjCObjectPointerType *OPT = 0; // catch(...) always matches. if (!CatchParam) { AllMatched = true; } else { OPT = CatchParam->getType()->getAs(); // catch(id e) always matches. // FIXME: For the time being we also match id; this should // be rejected by Sema instead. if (OPT && (OPT->isObjCIdType() || OPT->isObjCQualifiedIdType())) AllMatched = true; } if (AllMatched) { if (CatchParam) { CGF.EmitLocalBlockVarDecl(*CatchParam); assert(CGF.HaveInsertPoint() && "DeclStmt destroyed insert point?"); CGF.Builder.CreateStore(Caught, CGF.GetAddrOfLocalVar(CatchParam)); } CGF.EmitStmt(CatchStmt->getCatchBody()); CGF.EmitBranchThroughCleanup(FinallyEnd); break; } assert(OPT && "Unexpected non-object pointer type in @catch"); const ObjCObjectType *ObjTy = OPT->getObjectType(); ObjCInterfaceDecl *IDecl = ObjTy->getInterface(); assert(IDecl && "Catch parameter must have Objective-C type!"); // Check if the @catch block matches the exception object. llvm::Value *Class = EmitClassRef(CGF.Builder, IDecl); llvm::Value *Match = CGF.Builder.CreateCall2(ObjCTypes.getExceptionMatchFn(), Class, Caught, "match"); llvm::BasicBlock *MatchedBlock = CGF.createBasicBlock("matched"); CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNotNull(Match, "matched"), MatchedBlock, NextCatchBlock); // Emit the @catch block. CGF.EmitBlock(MatchedBlock); CGF.EmitLocalBlockVarDecl(*CatchParam); assert(CGF.HaveInsertPoint() && "DeclStmt destroyed insert point?"); llvm::Value *Tmp = CGF.Builder.CreateBitCast(Caught, CGF.ConvertType(CatchParam->getType()), "tmp"); CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(CatchParam)); CGF.EmitStmt(CatchStmt->getCatchBody()); CGF.EmitBranchThroughCleanup(FinallyEnd); CGF.EmitBlock(NextCatchBlock); } if (!AllMatched) { // None of the handlers caught the exception, so store it to be // rethrown at the end of the @finally block. CGF.Builder.CreateStore(Caught, RethrowPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); } // Emit the exception handler for the @catch blocks. CGF.EmitBlock(CatchHandler); CGF.Builder.CreateStore( CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(), ExceptionData), RethrowPtr); CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), CallTryExitPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); } else { CGF.Builder.CreateStore(Caught, RethrowPtr); CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), CallTryExitPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); } // Pop the exception-handling stack entry. It is important to do // this now, because the code in the @finally block is not in this // context. CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); CGF.ObjCEHValueStack.pop_back(); // Emit the @finally block. CGF.EmitBlock(FinallyBlock); llvm::Value* CallTryExit = CGF.Builder.CreateLoad(CallTryExitPtr, "tmp"); CGF.Builder.CreateCondBr(CallTryExit, FinallyExit, FinallyNoExit); CGF.EmitBlock(FinallyExit); CGF.Builder.CreateCall(ObjCTypes.getExceptionTryExitFn(), ExceptionData); CGF.EmitBlock(FinallyNoExit); if (isTry) { if (const ObjCAtFinallyStmt* FinallyStmt = cast(S).getFinallyStmt()) CGF.EmitStmt(FinallyStmt->getFinallyBody()); } else { // Emit objc_sync_exit(expr); as finally's sole statement for // @synchronized. CGF.Builder.CreateCall(ObjCTypes.getSyncExitFn(), SyncArg); } // Emit the switch block if (Info.SwitchBlock) CGF.EmitBlock(Info.SwitchBlock); if (Info.EndBlock) CGF.EmitBlock(Info.EndBlock); CGF.EmitBlock(FinallyRethrow); CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), CGF.Builder.CreateLoad(RethrowPtr)); CGF.Builder.CreateUnreachable(); CGF.EmitBlock(FinallyEnd); } void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S) { llvm::Value *ExceptionAsObject; if (const Expr *ThrowExpr = S.getThrowExpr()) { llvm::Value *Exception = CGF.EmitScalarExpr(ThrowExpr); ExceptionAsObject = CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp"); } else { assert((!CGF.ObjCEHValueStack.empty() && CGF.ObjCEHValueStack.back()) && "Unexpected rethrow outside @catch block."); ExceptionAsObject = CGF.ObjCEHValueStack.back(); } CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), ExceptionAsObject); CGF.Builder.CreateUnreachable(); // Clear the insertion point to indicate we are in unreachable code. CGF.Builder.ClearInsertionPoint(); } /// EmitObjCWeakRead - Code gen for loading value of a __weak /// object: objc_read_weak (id *src) /// llvm::Value * CGObjCMac::EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF, llvm::Value *AddrWeakObj) { const llvm::Type* DestTy = cast(AddrWeakObj->getType())->getElementType(); AddrWeakObj = CGF.Builder.CreateBitCast(AddrWeakObj, ObjCTypes.PtrObjectPtrTy); llvm::Value *read_weak = CGF.Builder.CreateCall(ObjCTypes.getGcReadWeakFn(), AddrWeakObj, "weakread"); read_weak = CGF.Builder.CreateBitCast(read_weak, DestTy); return read_weak; } /// EmitObjCWeakAssign - Code gen for assigning to a __weak object. /// objc_assign_weak (id src, id *dst) /// void CGObjCMac::EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4) ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongLongTy); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall2(ObjCTypes.getGcAssignWeakFn(), src, dst, "weakassign"); return; } /// EmitObjCGlobalAssign - Code gen for assigning to a __strong object. /// objc_assign_global (id src, id *dst) /// void CGObjCMac::EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4) ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongLongTy); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall2(ObjCTypes.getGcAssignGlobalFn(), src, dst, "globalassign"); return; } /// EmitObjCIvarAssign - Code gen for assigning to a __strong object. /// objc_assign_ivar (id src, id *dst, ptrdiff_t ivaroffset) /// void CGObjCMac::EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst, llvm::Value *ivarOffset) { assert(ivarOffset && "EmitObjCIvarAssign - ivarOffset is NULL"); const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4) ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongLongTy); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall3(ObjCTypes.getGcAssignIvarFn(), src, dst, ivarOffset); return; } /// EmitObjCStrongCastAssign - Code gen for assigning to a __strong cast object. /// objc_assign_strongCast (id src, id *dst) /// void CGObjCMac::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4) ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongLongTy); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall2(ObjCTypes.getGcAssignStrongCastFn(), src, dst, "weakassign"); return; } void CGObjCMac::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType Ty) { // Get size info for this aggregate. std::pair TypeInfo = CGM.getContext().getTypeInfo(Ty); unsigned long size = TypeInfo.first/8; SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, ObjCTypes.Int8PtrTy); DestPtr = CGF.Builder.CreateBitCast(DestPtr, ObjCTypes.Int8PtrTy); llvm::Value *N = llvm::ConstantInt::get(ObjCTypes.LongTy, size); CGF.Builder.CreateCall3(ObjCTypes.GcMemmoveCollectableFn(), DestPtr, SrcPtr, N); return; } /// EmitObjCValueForIvar - Code Gen for ivar reference. /// LValue CGObjCMac::EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy, llvm::Value *BaseValue, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers) { const ObjCInterfaceDecl *ID = ObjectTy->getAs()->getInterface(); return EmitValueForIvarAtOffset(CGF, ID, BaseValue, Ivar, CVRQualifiers, EmitIvarOffset(CGF, ID, Ivar)); } llvm::Value *CGObjCMac::EmitIvarOffset(CodeGen::CodeGenFunction &CGF, const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar) { uint64_t Offset = ComputeIvarBaseOffset(CGM, Interface, Ivar); return llvm::ConstantInt::get( CGM.getTypes().ConvertType(CGM.getContext().LongTy), Offset); } /* *** Private Interface *** */ /// EmitImageInfo - Emit the image info marker used to encode some module /// level information. /// /// See: /// struct IMAGE_INFO { /// unsigned version; /// unsigned flags; /// }; enum ImageInfoFlags { eImageInfo_FixAndContinue = (1 << 0), eImageInfo_GarbageCollected = (1 << 1), eImageInfo_GCOnly = (1 << 2), eImageInfo_OptimizedByDyld = (1 << 3), // FIXME: When is this set. // A flag indicating that the module has no instances of a @synthesize of a // superclass variable. eImageInfo_CorrectedSynthesize = (1 << 4) }; void CGObjCCommonMac::EmitImageInfo() { unsigned version = 0; // Version is unused? unsigned flags = 0; // FIXME: Fix and continue? if (CGM.getLangOptions().getGCMode() != LangOptions::NonGC) flags |= eImageInfo_GarbageCollected; if (CGM.getLangOptions().getGCMode() == LangOptions::GCOnly) flags |= eImageInfo_GCOnly; // We never allow @synthesize of a superclass property. flags |= eImageInfo_CorrectedSynthesize; // Emitted as int[2]; llvm::Constant *values[2] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), version), llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), flags) }; llvm::ArrayType *AT = llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), 2); const char *Section; if (ObjCABI == 1) Section = "__OBJC, __image_info,regular"; else Section = "__DATA, __objc_imageinfo, regular, no_dead_strip"; llvm::GlobalVariable *GV = CreateMetadataVar("\01L_OBJC_IMAGE_INFO", llvm::ConstantArray::get(AT, values, 2), Section, 0, true); GV->setConstant(true); } // struct objc_module { // unsigned long version; // unsigned long size; // const char *name; // Symtab symtab; // }; // FIXME: Get from somewhere static const int ModuleVersion = 7; void CGObjCMac::EmitModuleInfo() { uint64_t Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ModuleTy); std::vector Values(4); Values[0] = llvm::ConstantInt::get(ObjCTypes.LongTy, ModuleVersion); Values[1] = llvm::ConstantInt::get(ObjCTypes.LongTy, Size); // This used to be the filename, now it is unused. Values[2] = GetClassName(&CGM.getContext().Idents.get("")); Values[3] = EmitModuleSymbols(); CreateMetadataVar("\01L_OBJC_MODULES", llvm::ConstantStruct::get(ObjCTypes.ModuleTy, Values), "__OBJC,__module_info,regular,no_dead_strip", 4, true); } llvm::Constant *CGObjCMac::EmitModuleSymbols() { unsigned NumClasses = DefinedClasses.size(); unsigned NumCategories = DefinedCategories.size(); // Return null if no symbols were defined. if (!NumClasses && !NumCategories) return llvm::Constant::getNullValue(ObjCTypes.SymtabPtrTy); std::vector Values(5); Values[0] = llvm::ConstantInt::get(ObjCTypes.LongTy, 0); Values[1] = llvm::Constant::getNullValue(ObjCTypes.SelectorPtrTy); Values[2] = llvm::ConstantInt::get(ObjCTypes.ShortTy, NumClasses); Values[3] = llvm::ConstantInt::get(ObjCTypes.ShortTy, NumCategories); // The runtime expects exactly the list of defined classes followed // by the list of defined categories, in a single array. std::vector Symbols(NumClasses + NumCategories); for (unsigned i=0; igetIdentifier()); llvm::GlobalVariable *&Entry = ClassReferences[ID->getIdentifier()]; if (!Entry) { llvm::Constant *Casted = llvm::ConstantExpr::getBitCast(GetClassName(ID->getIdentifier()), ObjCTypes.ClassPtrTy); Entry = CreateMetadataVar("\01L_OBJC_CLASS_REFERENCES_", Casted, "__OBJC,__cls_refs,literal_pointers,no_dead_strip", 4, true); } return Builder.CreateLoad(Entry, "tmp"); } llvm::Value *CGObjCMac::EmitSelector(CGBuilderTy &Builder, Selector Sel) { llvm::GlobalVariable *&Entry = SelectorReferences[Sel]; if (!Entry) { llvm::Constant *Casted = llvm::ConstantExpr::getBitCast(GetMethodVarName(Sel), ObjCTypes.SelectorPtrTy); Entry = CreateMetadataVar("\01L_OBJC_SELECTOR_REFERENCES_", Casted, "__OBJC,__message_refs,literal_pointers,no_dead_strip", 4, true); } return Builder.CreateLoad(Entry, "tmp"); } llvm::Constant *CGObjCCommonMac::GetClassName(IdentifierInfo *Ident) { llvm::GlobalVariable *&Entry = ClassNames[Ident]; if (!Entry) Entry = CreateMetadataVar("\01L_OBJC_CLASS_NAME_", llvm::ConstantArray::get(VMContext, Ident->getNameStart()), "__TEXT,__cstring,cstring_literals", 1, true); return getConstantGEP(VMContext, Entry, 0, 0); } /// GetIvarLayoutName - Returns a unique constant for the given /// ivar layout bitmap. llvm::Constant *CGObjCCommonMac::GetIvarLayoutName(IdentifierInfo *Ident, const ObjCCommonTypesHelper &ObjCTypes) { return llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); } static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) { if (FQT.isObjCGCStrong()) return Qualifiers::Strong; if (FQT.isObjCGCWeak()) return Qualifiers::Weak; if (FQT->isObjCObjectPointerType() || FQT->isBlockPointerType()) return Qualifiers::Strong; if (const PointerType *PT = FQT->getAs()) return GetGCAttrTypeForType(Ctx, PT->getPointeeType()); return Qualifiers::GCNone; } void CGObjCCommonMac::BuildAggrIvarRecordLayout(const RecordType *RT, unsigned int BytePos, bool ForStrongLayout, bool &HasUnion) { const RecordDecl *RD = RT->getDecl(); // FIXME - Use iterator. llvm::SmallVector Fields(RD->field_begin(), RD->field_end()); const llvm::Type *Ty = CGM.getTypes().ConvertType(QualType(RT, 0)); const llvm::StructLayout *RecLayout = CGM.getTargetData().getStructLayout(cast(Ty)); BuildAggrIvarLayout(0, RecLayout, RD, Fields, BytePos, ForStrongLayout, HasUnion); } void CGObjCCommonMac::BuildAggrIvarLayout(const ObjCImplementationDecl *OI, const llvm::StructLayout *Layout, const RecordDecl *RD, const llvm::SmallVectorImpl &RecFields, unsigned int BytePos, bool ForStrongLayout, bool &HasUnion) { bool IsUnion = (RD && RD->isUnion()); uint64_t MaxUnionIvarSize = 0; uint64_t MaxSkippedUnionIvarSize = 0; FieldDecl *MaxField = 0; FieldDecl *MaxSkippedField = 0; FieldDecl *LastFieldBitfield = 0; uint64_t MaxFieldOffset = 0; uint64_t MaxSkippedFieldOffset = 0; uint64_t LastBitfieldOffset = 0; if (RecFields.empty()) return; unsigned WordSizeInBits = CGM.getContext().Target.getPointerWidth(0); unsigned ByteSizeInBits = CGM.getContext().Target.getCharWidth(); for (unsigned i = 0, e = RecFields.size(); i != e; ++i) { FieldDecl *Field = RecFields[i]; uint64_t FieldOffset; if (RD) { // Note that 'i' here is actually the field index inside RD of Field, // although this dependency is hidden. const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); FieldOffset = RL.getFieldOffset(i) / 8; } else FieldOffset = ComputeIvarBaseOffset(CGM, OI, cast(Field)); // Skip over unnamed or bitfields if (!Field->getIdentifier() || Field->isBitField()) { LastFieldBitfield = Field; LastBitfieldOffset = FieldOffset; continue; } LastFieldBitfield = 0; QualType FQT = Field->getType(); if (FQT->isRecordType() || FQT->isUnionType()) { if (FQT->isUnionType()) HasUnion = true; BuildAggrIvarRecordLayout(FQT->getAs(), BytePos + FieldOffset, ForStrongLayout, HasUnion); continue; } if (const ArrayType *Array = CGM.getContext().getAsArrayType(FQT)) { const ConstantArrayType *CArray = dyn_cast_or_null(Array); uint64_t ElCount = CArray->getSize().getZExtValue(); assert(CArray && "only array with known element size is supported"); FQT = CArray->getElementType(); while (const ArrayType *Array = CGM.getContext().getAsArrayType(FQT)) { const ConstantArrayType *CArray = dyn_cast_or_null(Array); ElCount *= CArray->getSize().getZExtValue(); FQT = CArray->getElementType(); } assert(!FQT->isUnionType() && "layout for array of unions not supported"); if (FQT->isRecordType()) { int OldIndex = IvarsInfo.size() - 1; int OldSkIndex = SkipIvars.size() -1; const RecordType *RT = FQT->getAs(); BuildAggrIvarRecordLayout(RT, BytePos + FieldOffset, ForStrongLayout, HasUnion); // Replicate layout information for each array element. Note that // one element is already done. uint64_t ElIx = 1; for (int FirstIndex = IvarsInfo.size() - 1, FirstSkIndex = SkipIvars.size() - 1 ;ElIx < ElCount; ElIx++) { uint64_t Size = CGM.getContext().getTypeSize(RT)/ByteSizeInBits; for (int i = OldIndex+1; i <= FirstIndex; ++i) IvarsInfo.push_back(GC_IVAR(IvarsInfo[i].ivar_bytepos + Size*ElIx, IvarsInfo[i].ivar_size)); for (int i = OldSkIndex+1; i <= FirstSkIndex; ++i) SkipIvars.push_back(GC_IVAR(SkipIvars[i].ivar_bytepos + Size*ElIx, SkipIvars[i].ivar_size)); } continue; } } // At this point, we are done with Record/Union and array there of. // For other arrays we are down to its element type. Qualifiers::GC GCAttr = GetGCAttrTypeForType(CGM.getContext(), FQT); unsigned FieldSize = CGM.getContext().getTypeSize(Field->getType()); if ((ForStrongLayout && GCAttr == Qualifiers::Strong) || (!ForStrongLayout && GCAttr == Qualifiers::Weak)) { if (IsUnion) { uint64_t UnionIvarSize = FieldSize / WordSizeInBits; if (UnionIvarSize > MaxUnionIvarSize) { MaxUnionIvarSize = UnionIvarSize; MaxField = Field; MaxFieldOffset = FieldOffset; } } else { IvarsInfo.push_back(GC_IVAR(BytePos + FieldOffset, FieldSize / WordSizeInBits)); } } else if ((ForStrongLayout && (GCAttr == Qualifiers::GCNone || GCAttr == Qualifiers::Weak)) || (!ForStrongLayout && GCAttr != Qualifiers::Weak)) { if (IsUnion) { // FIXME: Why the asymmetry? We divide by word size in bits on other // side. uint64_t UnionIvarSize = FieldSize; if (UnionIvarSize > MaxSkippedUnionIvarSize) { MaxSkippedUnionIvarSize = UnionIvarSize; MaxSkippedField = Field; MaxSkippedFieldOffset = FieldOffset; } } else { // FIXME: Why the asymmetry, we divide by byte size in bits here? SkipIvars.push_back(GC_IVAR(BytePos + FieldOffset, FieldSize / ByteSizeInBits)); } } } if (LastFieldBitfield) { // Last field was a bitfield. Must update skip info. Expr *BitWidth = LastFieldBitfield->getBitWidth(); uint64_t BitFieldSize = BitWidth->EvaluateAsInt(CGM.getContext()).getZExtValue(); GC_IVAR skivar; skivar.ivar_bytepos = BytePos + LastBitfieldOffset; skivar.ivar_size = (BitFieldSize / ByteSizeInBits) + ((BitFieldSize % ByteSizeInBits) != 0); SkipIvars.push_back(skivar); } if (MaxField) IvarsInfo.push_back(GC_IVAR(BytePos + MaxFieldOffset, MaxUnionIvarSize)); if (MaxSkippedField) SkipIvars.push_back(GC_IVAR(BytePos + MaxSkippedFieldOffset, MaxSkippedUnionIvarSize)); } /// BuildIvarLayout - Builds ivar layout bitmap for the class /// implementation for the __strong or __weak case. /// The layout map displays which words in ivar list must be skipped /// and which must be scanned by GC (see below). String is built of bytes. /// Each byte is divided up in two nibbles (4-bit each). Left nibble is count /// of words to skip and right nibble is count of words to scan. So, each /// nibble represents up to 15 workds to skip or scan. Skipping the rest is /// represented by a 0x00 byte which also ends the string. /// 1. when ForStrongLayout is true, following ivars are scanned: /// - id, Class /// - object * /// - __strong anything /// /// 2. When ForStrongLayout is false, following ivars are scanned: /// - __weak anything /// llvm::Constant *CGObjCCommonMac::BuildIvarLayout( const ObjCImplementationDecl *OMD, bool ForStrongLayout) { bool hasUnion = false; unsigned int WordsToScan, WordsToSkip; const llvm::Type *PtrTy = llvm::Type::getInt8PtrTy(VMContext); if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) return llvm::Constant::getNullValue(PtrTy); llvm::SmallVector RecFields; const ObjCInterfaceDecl *OI = OMD->getClassInterface(); CGM.getContext().CollectObjCIvars(OI, RecFields); // Add this implementations synthesized ivars. llvm::SmallVector Ivars; CGM.getContext().CollectNonClassIvars(OI, Ivars); for (unsigned k = 0, e = Ivars.size(); k != e; ++k) RecFields.push_back(cast(Ivars[k])); if (RecFields.empty()) return llvm::Constant::getNullValue(PtrTy); SkipIvars.clear(); IvarsInfo.clear(); BuildAggrIvarLayout(OMD, 0, 0, RecFields, 0, ForStrongLayout, hasUnion); if (IvarsInfo.empty()) return llvm::Constant::getNullValue(PtrTy); // Sort on byte position in case we encounterred a union nested in // the ivar list. if (hasUnion && !IvarsInfo.empty()) std::sort(IvarsInfo.begin(), IvarsInfo.end()); if (hasUnion && !SkipIvars.empty()) std::sort(SkipIvars.begin(), SkipIvars.end()); // Build the string of skip/scan nibbles llvm::SmallVector SkipScanIvars; unsigned int WordSize = CGM.getTypes().getTargetData().getTypeAllocSize(PtrTy); if (IvarsInfo[0].ivar_bytepos == 0) { WordsToSkip = 0; WordsToScan = IvarsInfo[0].ivar_size; } else { WordsToSkip = IvarsInfo[0].ivar_bytepos/WordSize; WordsToScan = IvarsInfo[0].ivar_size; } for (unsigned int i=1, Last=IvarsInfo.size(); i != Last; i++) { unsigned int TailPrevGCObjC = IvarsInfo[i-1].ivar_bytepos + IvarsInfo[i-1].ivar_size * WordSize; if (IvarsInfo[i].ivar_bytepos == TailPrevGCObjC) { // consecutive 'scanned' object pointers. WordsToScan += IvarsInfo[i].ivar_size; } else { // Skip over 'gc'able object pointer which lay over each other. if (TailPrevGCObjC > IvarsInfo[i].ivar_bytepos) continue; // Must skip over 1 or more words. We save current skip/scan values // and start a new pair. SKIP_SCAN SkScan; SkScan.skip = WordsToSkip; SkScan.scan = WordsToScan; SkipScanIvars.push_back(SkScan); // Skip the hole. SkScan.skip = (IvarsInfo[i].ivar_bytepos - TailPrevGCObjC) / WordSize; SkScan.scan = 0; SkipScanIvars.push_back(SkScan); WordsToSkip = 0; WordsToScan = IvarsInfo[i].ivar_size; } } if (WordsToScan > 0) { SKIP_SCAN SkScan; SkScan.skip = WordsToSkip; SkScan.scan = WordsToScan; SkipScanIvars.push_back(SkScan); } if (!SkipIvars.empty()) { unsigned int LastIndex = SkipIvars.size()-1; int LastByteSkipped = SkipIvars[LastIndex].ivar_bytepos + SkipIvars[LastIndex].ivar_size; LastIndex = IvarsInfo.size()-1; int LastByteScanned = IvarsInfo[LastIndex].ivar_bytepos + IvarsInfo[LastIndex].ivar_size * WordSize; // Compute number of bytes to skip at the tail end of the last ivar scanned. if (LastByteSkipped > LastByteScanned) { unsigned int TotalWords = (LastByteSkipped + (WordSize -1)) / WordSize; SKIP_SCAN SkScan; SkScan.skip = TotalWords - (LastByteScanned/WordSize); SkScan.scan = 0; SkipScanIvars.push_back(SkScan); } } // Mini optimization of nibbles such that an 0xM0 followed by 0x0N is produced // as 0xMN. int SkipScan = SkipScanIvars.size()-1; for (int i = 0; i <= SkipScan; i++) { if ((i < SkipScan) && SkipScanIvars[i].skip && SkipScanIvars[i].scan == 0 && SkipScanIvars[i+1].skip == 0 && SkipScanIvars[i+1].scan) { // 0xM0 followed by 0x0N detected. SkipScanIvars[i].scan = SkipScanIvars[i+1].scan; for (int j = i+1; j < SkipScan; j++) SkipScanIvars[j] = SkipScanIvars[j+1]; --SkipScan; } } // Generate the string. std::string BitMap; for (int i = 0; i <= SkipScan; i++) { unsigned char byte; unsigned int skip_small = SkipScanIvars[i].skip % 0xf; unsigned int scan_small = SkipScanIvars[i].scan % 0xf; unsigned int skip_big = SkipScanIvars[i].skip / 0xf; unsigned int scan_big = SkipScanIvars[i].scan / 0xf; // first skip big. for (unsigned int ix = 0; ix < skip_big; ix++) BitMap += (unsigned char)(0xf0); // next (skip small, scan) if (skip_small) { byte = skip_small << 4; if (scan_big > 0) { byte |= 0xf; --scan_big; } else if (scan_small) { byte |= scan_small; scan_small = 0; } BitMap += byte; } // next scan big for (unsigned int ix = 0; ix < scan_big; ix++) BitMap += (unsigned char)(0x0f); // last scan small if (scan_small) { byte = scan_small; BitMap += byte; } } // null terminate string. unsigned char zero = 0; BitMap += zero; if (CGM.getLangOptions().ObjCGCBitmapPrint) { printf("\n%s ivar layout for class '%s': ", ForStrongLayout ? "strong" : "weak", OMD->getClassInterface()->getNameAsCString()); const unsigned char *s = (unsigned char*)BitMap.c_str(); for (unsigned i = 0; i < BitMap.size(); i++) if (!(s[i] & 0xf0)) printf("0x0%x%s", s[i], s[i] != 0 ? ", " : ""); else printf("0x%x%s", s[i], s[i] != 0 ? ", " : ""); printf("\n"); } llvm::GlobalVariable * Entry = CreateMetadataVar("\01L_OBJC_CLASS_NAME_", llvm::ConstantArray::get(VMContext, BitMap.c_str()), "__TEXT,__cstring,cstring_literals", 1, true); return getConstantGEP(VMContext, Entry, 0, 0); } llvm::Constant *CGObjCCommonMac::GetMethodVarName(Selector Sel) { llvm::GlobalVariable *&Entry = MethodVarNames[Sel]; // FIXME: Avoid std::string copying. if (!Entry) Entry = CreateMetadataVar("\01L_OBJC_METH_VAR_NAME_", llvm::ConstantArray::get(VMContext, Sel.getAsString()), "__TEXT,__cstring,cstring_literals", 1, true); return getConstantGEP(VMContext, Entry, 0, 0); } // FIXME: Merge into a single cstring creation function. llvm::Constant *CGObjCCommonMac::GetMethodVarName(IdentifierInfo *ID) { return GetMethodVarName(CGM.getContext().Selectors.getNullarySelector(ID)); } // FIXME: Merge into a single cstring creation function. llvm::Constant *CGObjCCommonMac::GetMethodVarName(const std::string &Name) { return GetMethodVarName(&CGM.getContext().Idents.get(Name)); } llvm::Constant *CGObjCCommonMac::GetMethodVarType(const FieldDecl *Field) { std::string TypeStr; CGM.getContext().getObjCEncodingForType(Field->getType(), TypeStr, Field); llvm::GlobalVariable *&Entry = MethodVarTypes[TypeStr]; if (!Entry) Entry = CreateMetadataVar("\01L_OBJC_METH_VAR_TYPE_", llvm::ConstantArray::get(VMContext, TypeStr), "__TEXT,__cstring,cstring_literals", 1, true); return getConstantGEP(VMContext, Entry, 0, 0); } llvm::Constant *CGObjCCommonMac::GetMethodVarType(const ObjCMethodDecl *D) { std::string TypeStr; CGM.getContext().getObjCEncodingForMethodDecl(const_cast(D), TypeStr); llvm::GlobalVariable *&Entry = MethodVarTypes[TypeStr]; if (!Entry) Entry = CreateMetadataVar("\01L_OBJC_METH_VAR_TYPE_", llvm::ConstantArray::get(VMContext, TypeStr), "__TEXT,__cstring,cstring_literals", 1, true); return getConstantGEP(VMContext, Entry, 0, 0); } // FIXME: Merge into a single cstring creation function. llvm::Constant *CGObjCCommonMac::GetPropertyName(IdentifierInfo *Ident) { llvm::GlobalVariable *&Entry = PropertyNames[Ident]; if (!Entry) Entry = CreateMetadataVar("\01L_OBJC_PROP_NAME_ATTR_", llvm::ConstantArray::get(VMContext, Ident->getNameStart()), "__TEXT,__cstring,cstring_literals", 1, true); return getConstantGEP(VMContext, Entry, 0, 0); } // FIXME: Merge into a single cstring creation function. // FIXME: This Decl should be more precise. llvm::Constant * CGObjCCommonMac::GetPropertyTypeString(const ObjCPropertyDecl *PD, const Decl *Container) { std::string TypeStr; CGM.getContext().getObjCEncodingForPropertyDecl(PD, Container, TypeStr); return GetPropertyName(&CGM.getContext().Idents.get(TypeStr)); } void CGObjCCommonMac::GetNameForMethod(const ObjCMethodDecl *D, const ObjCContainerDecl *CD, llvm::SmallVectorImpl &Name) { llvm::raw_svector_ostream OS(Name); assert (CD && "Missing container decl in GetNameForMethod"); OS << '\01' << (D->isInstanceMethod() ? '-' : '+') << '[' << CD->getName(); if (const ObjCCategoryImplDecl *CID = dyn_cast(D->getDeclContext())) OS << '(' << CID << ')'; OS << ' ' << D->getSelector().getAsString() << ']'; } void CGObjCMac::FinishModule() { EmitModuleInfo(); // Emit the dummy bodies for any protocols which were referenced but // never defined. for (llvm::DenseMap::iterator I = Protocols.begin(), e = Protocols.end(); I != e; ++I) { if (I->second->hasInitializer()) continue; std::vector Values(5); Values[0] = llvm::Constant::getNullValue(ObjCTypes.ProtocolExtensionPtrTy); Values[1] = GetClassName(I->first); Values[2] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy); Values[3] = Values[4] = llvm::Constant::getNullValue(ObjCTypes.MethodDescriptionListPtrTy); I->second->setLinkage(llvm::GlobalValue::InternalLinkage); I->second->setInitializer(llvm::ConstantStruct::get(ObjCTypes.ProtocolTy, Values)); CGM.AddUsedGlobal(I->second); } // Add assembler directives to add lazy undefined symbol references // for classes which are referenced but not defined. This is // important for correct linker interaction. // // FIXME: It would be nice if we had an LLVM construct for this. if (!LazySymbols.empty() || !DefinedSymbols.empty()) { llvm::SmallString<256> Asm; Asm += CGM.getModule().getModuleInlineAsm(); if (!Asm.empty() && Asm.back() != '\n') Asm += '\n'; llvm::raw_svector_ostream OS(Asm); for (llvm::SetVector::iterator I = DefinedSymbols.begin(), e = DefinedSymbols.end(); I != e; ++I) OS << "\t.objc_class_name_" << (*I)->getName() << "=0\n" << "\t.globl .objc_class_name_" << (*I)->getName() << "\n"; for (llvm::SetVector::iterator I = LazySymbols.begin(), e = LazySymbols.end(); I != e; ++I) OS << "\t.lazy_reference .objc_class_name_" << (*I)->getName() << "\n"; CGM.getModule().setModuleInlineAsm(OS.str()); } } CGObjCNonFragileABIMac::CGObjCNonFragileABIMac(CodeGen::CodeGenModule &cgm) : CGObjCCommonMac(cgm), ObjCTypes(cgm) { ObjCEmptyCacheVar = ObjCEmptyVtableVar = NULL; ObjCABI = 2; } /* *** */ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm) : VMContext(cgm.getLLVMContext()), CGM(cgm) { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); ShortTy = Types.ConvertType(Ctx.ShortTy); IntTy = Types.ConvertType(Ctx.IntTy); LongTy = Types.ConvertType(Ctx.LongTy); LongLongTy = Types.ConvertType(Ctx.LongLongTy); Int8PtrTy = llvm::Type::getInt8PtrTy(VMContext); ObjectPtrTy = Types.ConvertType(Ctx.getObjCIdType()); PtrObjectPtrTy = llvm::PointerType::getUnqual(ObjectPtrTy); SelectorPtrTy = Types.ConvertType(Ctx.getObjCSelType()); // FIXME: It would be nice to unify this with the opaque type, so that the IR // comes out a bit cleaner. const llvm::Type *T = Types.ConvertType(Ctx.getObjCProtoType()); ExternalProtocolPtrTy = llvm::PointerType::getUnqual(T); // I'm not sure I like this. The implicit coordination is a bit // gross. We should solve this in a reasonable fashion because this // is a pretty common task (match some runtime data structure with // an LLVM data structure). // FIXME: This is leaked. // FIXME: Merge with rewriter code? // struct _objc_super { // id self; // Class cls; // } RecordDecl *RD = RecordDecl::Create(Ctx, TTK_Struct, Ctx.getTranslationUnitDecl(), SourceLocation(), &Ctx.Idents.get("_objc_super")); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), 0, Ctx.getObjCIdType(), 0, 0, false)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), 0, Ctx.getObjCClassType(), 0, 0, false)); RD->completeDefinition(); SuperCTy = Ctx.getTagDeclType(RD); SuperPtrCTy = Ctx.getPointerType(SuperCTy); SuperTy = cast(Types.ConvertType(SuperCTy)); SuperPtrTy = llvm::PointerType::getUnqual(SuperTy); // struct _prop_t { // char *name; // char *attributes; // } PropertyTy = llvm::StructType::get(VMContext, Int8PtrTy, Int8PtrTy, NULL); CGM.getModule().addTypeName("struct._prop_t", PropertyTy); // struct _prop_list_t { // uint32_t entsize; // sizeof(struct _prop_t) // uint32_t count_of_properties; // struct _prop_t prop_list[count_of_properties]; // } PropertyListTy = llvm::StructType::get(VMContext, IntTy, IntTy, llvm::ArrayType::get(PropertyTy, 0), NULL); CGM.getModule().addTypeName("struct._prop_list_t", PropertyListTy); // struct _prop_list_t * PropertyListPtrTy = llvm::PointerType::getUnqual(PropertyListTy); // struct _objc_method { // SEL _cmd; // char *method_type; // char *_imp; // } MethodTy = llvm::StructType::get(VMContext, SelectorPtrTy, Int8PtrTy, Int8PtrTy, NULL); CGM.getModule().addTypeName("struct._objc_method", MethodTy); // struct _objc_cache * CacheTy = llvm::OpaqueType::get(VMContext); CGM.getModule().addTypeName("struct._objc_cache", CacheTy); CachePtrTy = llvm::PointerType::getUnqual(CacheTy); } ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) : ObjCCommonTypesHelper(cgm) { // struct _objc_method_description { // SEL name; // char *types; // } MethodDescriptionTy = llvm::StructType::get(VMContext, SelectorPtrTy, Int8PtrTy, NULL); CGM.getModule().addTypeName("struct._objc_method_description", MethodDescriptionTy); // struct _objc_method_description_list { // int count; // struct _objc_method_description[1]; // } MethodDescriptionListTy = llvm::StructType::get(VMContext, IntTy, llvm::ArrayType::get(MethodDescriptionTy, 0), NULL); CGM.getModule().addTypeName("struct._objc_method_description_list", MethodDescriptionListTy); // struct _objc_method_description_list * MethodDescriptionListPtrTy = llvm::PointerType::getUnqual(MethodDescriptionListTy); // Protocol description structures // struct _objc_protocol_extension { // uint32_t size; // sizeof(struct _objc_protocol_extension) // struct _objc_method_description_list *optional_instance_methods; // struct _objc_method_description_list *optional_class_methods; // struct _objc_property_list *instance_properties; // } ProtocolExtensionTy = llvm::StructType::get(VMContext, IntTy, MethodDescriptionListPtrTy, MethodDescriptionListPtrTy, PropertyListPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_protocol_extension", ProtocolExtensionTy); // struct _objc_protocol_extension * ProtocolExtensionPtrTy = llvm::PointerType::getUnqual(ProtocolExtensionTy); // Handle recursive construction of Protocol and ProtocolList types llvm::PATypeHolder ProtocolTyHolder = llvm::OpaqueType::get(VMContext); llvm::PATypeHolder ProtocolListTyHolder = llvm::OpaqueType::get(VMContext); const llvm::Type *T = llvm::StructType::get(VMContext, llvm::PointerType::getUnqual(ProtocolListTyHolder), LongTy, llvm::ArrayType::get(ProtocolTyHolder, 0), NULL); cast(ProtocolListTyHolder.get())->refineAbstractTypeTo(T); // struct _objc_protocol { // struct _objc_protocol_extension *isa; // char *protocol_name; // struct _objc_protocol **_objc_protocol_list; // struct _objc_method_description_list *instance_methods; // struct _objc_method_description_list *class_methods; // } T = llvm::StructType::get(VMContext, ProtocolExtensionPtrTy, Int8PtrTy, llvm::PointerType::getUnqual(ProtocolListTyHolder), MethodDescriptionListPtrTy, MethodDescriptionListPtrTy, NULL); cast(ProtocolTyHolder.get())->refineAbstractTypeTo(T); ProtocolListTy = cast(ProtocolListTyHolder.get()); CGM.getModule().addTypeName("struct._objc_protocol_list", ProtocolListTy); // struct _objc_protocol_list * ProtocolListPtrTy = llvm::PointerType::getUnqual(ProtocolListTy); ProtocolTy = cast(ProtocolTyHolder.get()); CGM.getModule().addTypeName("struct._objc_protocol", ProtocolTy); ProtocolPtrTy = llvm::PointerType::getUnqual(ProtocolTy); // Class description structures // struct _objc_ivar { // char *ivar_name; // char *ivar_type; // int ivar_offset; // } IvarTy = llvm::StructType::get(VMContext, Int8PtrTy, Int8PtrTy, IntTy, NULL); CGM.getModule().addTypeName("struct._objc_ivar", IvarTy); // struct _objc_ivar_list * IvarListTy = llvm::OpaqueType::get(VMContext); CGM.getModule().addTypeName("struct._objc_ivar_list", IvarListTy); IvarListPtrTy = llvm::PointerType::getUnqual(IvarListTy); // struct _objc_method_list * MethodListTy = llvm::OpaqueType::get(VMContext); CGM.getModule().addTypeName("struct._objc_method_list", MethodListTy); MethodListPtrTy = llvm::PointerType::getUnqual(MethodListTy); // struct _objc_class_extension * ClassExtensionTy = llvm::StructType::get(VMContext, IntTy, Int8PtrTy, PropertyListPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_class_extension", ClassExtensionTy); ClassExtensionPtrTy = llvm::PointerType::getUnqual(ClassExtensionTy); llvm::PATypeHolder ClassTyHolder = llvm::OpaqueType::get(VMContext); // struct _objc_class { // Class isa; // Class super_class; // char *name; // long version; // long info; // long instance_size; // struct _objc_ivar_list *ivars; // struct _objc_method_list *methods; // struct _objc_cache *cache; // struct _objc_protocol_list *protocols; // char *ivar_layout; // struct _objc_class_ext *ext; // }; T = llvm::StructType::get(VMContext, llvm::PointerType::getUnqual(ClassTyHolder), llvm::PointerType::getUnqual(ClassTyHolder), Int8PtrTy, LongTy, LongTy, LongTy, IvarListPtrTy, MethodListPtrTy, CachePtrTy, ProtocolListPtrTy, Int8PtrTy, ClassExtensionPtrTy, NULL); cast(ClassTyHolder.get())->refineAbstractTypeTo(T); ClassTy = cast(ClassTyHolder.get()); CGM.getModule().addTypeName("struct._objc_class", ClassTy); ClassPtrTy = llvm::PointerType::getUnqual(ClassTy); // struct _objc_category { // char *category_name; // char *class_name; // struct _objc_method_list *instance_method; // struct _objc_method_list *class_method; // uint32_t size; // sizeof(struct _objc_category) // struct _objc_property_list *instance_properties;// category's @property // } CategoryTy = llvm::StructType::get(VMContext, Int8PtrTy, Int8PtrTy, MethodListPtrTy, MethodListPtrTy, ProtocolListPtrTy, IntTy, PropertyListPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_category", CategoryTy); // Global metadata structures // struct _objc_symtab { // long sel_ref_cnt; // SEL *refs; // short cls_def_cnt; // short cat_def_cnt; // char *defs[cls_def_cnt + cat_def_cnt]; // } SymtabTy = llvm::StructType::get(VMContext, LongTy, SelectorPtrTy, ShortTy, ShortTy, llvm::ArrayType::get(Int8PtrTy, 0), NULL); CGM.getModule().addTypeName("struct._objc_symtab", SymtabTy); SymtabPtrTy = llvm::PointerType::getUnqual(SymtabTy); // struct _objc_module { // long version; // long size; // sizeof(struct _objc_module) // char *name; // struct _objc_symtab* symtab; // } ModuleTy = llvm::StructType::get(VMContext, LongTy, LongTy, Int8PtrTy, SymtabPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_module", ModuleTy); // FIXME: This is the size of the setjmp buffer and should be target // specific. 18 is what's used on 32-bit X86. uint64_t SetJmpBufferSize = 18; // Exceptions const llvm::Type *StackPtrTy = llvm::ArrayType::get( llvm::Type::getInt8PtrTy(VMContext), 4); ExceptionDataTy = llvm::StructType::get(VMContext, llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), SetJmpBufferSize), StackPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_exception_data", ExceptionDataTy); } ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModule &cgm) : ObjCCommonTypesHelper(cgm) { // struct _method_list_t { // uint32_t entsize; // sizeof(struct _objc_method) // uint32_t method_count; // struct _objc_method method_list[method_count]; // } MethodListnfABITy = llvm::StructType::get(VMContext, IntTy, IntTy, llvm::ArrayType::get(MethodTy, 0), NULL); CGM.getModule().addTypeName("struct.__method_list_t", MethodListnfABITy); // struct method_list_t * MethodListnfABIPtrTy = llvm::PointerType::getUnqual(MethodListnfABITy); // struct _protocol_t { // id isa; // NULL // const char * const protocol_name; // const struct _protocol_list_t * protocol_list; // super protocols // const struct method_list_t * const instance_methods; // const struct method_list_t * const class_methods; // const struct method_list_t *optionalInstanceMethods; // const struct method_list_t *optionalClassMethods; // const struct _prop_list_t * properties; // const uint32_t size; // sizeof(struct _protocol_t) // const uint32_t flags; // = 0 // } // Holder for struct _protocol_list_t * llvm::PATypeHolder ProtocolListTyHolder = llvm::OpaqueType::get(VMContext); ProtocolnfABITy = llvm::StructType::get(VMContext, ObjectPtrTy, Int8PtrTy, llvm::PointerType::getUnqual( ProtocolListTyHolder), MethodListnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy, PropertyListPtrTy, IntTy, IntTy, NULL); CGM.getModule().addTypeName("struct._protocol_t", ProtocolnfABITy); // struct _protocol_t* ProtocolnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolnfABITy); // struct _protocol_list_t { // long protocol_count; // Note, this is 32/64 bit // struct _protocol_t *[protocol_count]; // } ProtocolListnfABITy = llvm::StructType::get(VMContext, LongTy, llvm::ArrayType::get( ProtocolnfABIPtrTy, 0), NULL); CGM.getModule().addTypeName("struct._objc_protocol_list", ProtocolListnfABITy); cast(ProtocolListTyHolder.get())->refineAbstractTypeTo( ProtocolListnfABITy); // struct _objc_protocol_list* ProtocolListnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolListnfABITy); // struct _ivar_t { // unsigned long int *offset; // pointer to ivar offset location // char *name; // char *type; // uint32_t alignment; // uint32_t size; // } IvarnfABITy = llvm::StructType::get(VMContext, llvm::PointerType::getUnqual(LongTy), Int8PtrTy, Int8PtrTy, IntTy, IntTy, NULL); CGM.getModule().addTypeName("struct._ivar_t", IvarnfABITy); // struct _ivar_list_t { // uint32 entsize; // sizeof(struct _ivar_t) // uint32 count; // struct _iver_t list[count]; // } IvarListnfABITy = llvm::StructType::get(VMContext, IntTy, IntTy, llvm::ArrayType::get( IvarnfABITy, 0), NULL); CGM.getModule().addTypeName("struct._ivar_list_t", IvarListnfABITy); IvarListnfABIPtrTy = llvm::PointerType::getUnqual(IvarListnfABITy); // struct _class_ro_t { // uint32_t const flags; // uint32_t const instanceStart; // uint32_t const instanceSize; // uint32_t const reserved; // only when building for 64bit targets // const uint8_t * const ivarLayout; // const char *const name; // const struct _method_list_t * const baseMethods; // const struct _objc_protocol_list *const baseProtocols; // const struct _ivar_list_t *const ivars; // const uint8_t * const weakIvarLayout; // const struct _prop_list_t * const properties; // } // FIXME. Add 'reserved' field in 64bit abi mode! ClassRonfABITy = llvm::StructType::get(VMContext, IntTy, IntTy, IntTy, Int8PtrTy, Int8PtrTy, MethodListnfABIPtrTy, ProtocolListnfABIPtrTy, IvarListnfABIPtrTy, Int8PtrTy, PropertyListPtrTy, NULL); CGM.getModule().addTypeName("struct._class_ro_t", ClassRonfABITy); // ImpnfABITy - LLVM for id (*)(id, SEL, ...) std::vector Params; Params.push_back(ObjectPtrTy); Params.push_back(SelectorPtrTy); ImpnfABITy = llvm::PointerType::getUnqual( llvm::FunctionType::get(ObjectPtrTy, Params, false)); // struct _class_t { // struct _class_t *isa; // struct _class_t * const superclass; // void *cache; // IMP *vtable; // struct class_ro_t *ro; // } llvm::PATypeHolder ClassTyHolder = llvm::OpaqueType::get(VMContext); ClassnfABITy = llvm::StructType::get(VMContext, llvm::PointerType::getUnqual(ClassTyHolder), llvm::PointerType::getUnqual(ClassTyHolder), CachePtrTy, llvm::PointerType::getUnqual(ImpnfABITy), llvm::PointerType::getUnqual(ClassRonfABITy), NULL); CGM.getModule().addTypeName("struct._class_t", ClassnfABITy); cast(ClassTyHolder.get())->refineAbstractTypeTo( ClassnfABITy); // LLVM for struct _class_t * ClassnfABIPtrTy = llvm::PointerType::getUnqual(ClassnfABITy); // struct _category_t { // const char * const name; // struct _class_t *const cls; // const struct _method_list_t * const instance_methods; // const struct _method_list_t * const class_methods; // const struct _protocol_list_t * const protocols; // const struct _prop_list_t * const properties; // } CategorynfABITy = llvm::StructType::get(VMContext, Int8PtrTy, ClassnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy, ProtocolListnfABIPtrTy, PropertyListPtrTy, NULL); CGM.getModule().addTypeName("struct._category_t", CategorynfABITy); // New types for nonfragile abi messaging. CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); // MessageRefTy - LLVM for: // struct _message_ref_t { // IMP messenger; // SEL name; // }; // First the clang type for struct _message_ref_t RecordDecl *RD = RecordDecl::Create(Ctx, TTK_Struct, Ctx.getTranslationUnitDecl(), SourceLocation(), &Ctx.Idents.get("_message_ref_t")); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), 0, Ctx.VoidPtrTy, 0, 0, false)); RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), 0, Ctx.getObjCSelType(), 0, 0, false)); RD->completeDefinition(); MessageRefCTy = Ctx.getTagDeclType(RD); MessageRefCPtrTy = Ctx.getPointerType(MessageRefCTy); MessageRefTy = cast(Types.ConvertType(MessageRefCTy)); // MessageRefPtrTy - LLVM for struct _message_ref_t* MessageRefPtrTy = llvm::PointerType::getUnqual(MessageRefTy); // SuperMessageRefTy - LLVM for: // struct _super_message_ref_t { // SUPER_IMP messenger; // SEL name; // }; SuperMessageRefTy = llvm::StructType::get(VMContext, ImpnfABITy, SelectorPtrTy, NULL); CGM.getModule().addTypeName("struct._super_message_ref_t", SuperMessageRefTy); // SuperMessageRefPtrTy - LLVM for struct _super_message_ref_t* SuperMessageRefPtrTy = llvm::PointerType::getUnqual(SuperMessageRefTy); // struct objc_typeinfo { // const void** vtable; // objc_ehtype_vtable + 2 // const char* name; // c++ typeinfo string // Class cls; // }; EHTypeTy = llvm::StructType::get(VMContext, llvm::PointerType::getUnqual(Int8PtrTy), Int8PtrTy, ClassnfABIPtrTy, NULL); CGM.getModule().addTypeName("struct._objc_typeinfo", EHTypeTy); EHTypePtrTy = llvm::PointerType::getUnqual(EHTypeTy); } llvm::Function *CGObjCNonFragileABIMac::ModuleInitFunction() { FinishNonFragileABIModule(); return NULL; } void CGObjCNonFragileABIMac::AddModuleClassList(const std::vector &Container, const char *SymbolName, const char *SectionName) { unsigned NumClasses = Container.size(); if (!NumClasses) return; std::vector Symbols(NumClasses); for (unsigned i=0; igetType(), false, llvm::GlobalValue::InternalLinkage, Init, SymbolName); GV->setAlignment(CGM.getTargetData().getABITypeAlignment(Init->getType())); GV->setSection(SectionName); CGM.AddUsedGlobal(GV); } void CGObjCNonFragileABIMac::FinishNonFragileABIModule() { // nonfragile abi has no module definition. // Build list of all implemented class addresses in array // L_OBJC_LABEL_CLASS_$. AddModuleClassList(DefinedClasses, "\01L_OBJC_LABEL_CLASS_$", "__DATA, __objc_classlist, regular, no_dead_strip"); for (unsigned i = 0; i < DefinedClasses.size(); i++) { llvm::GlobalValue *IMPLGV = DefinedClasses[i]; if (IMPLGV->getLinkage() != llvm::GlobalValue::ExternalWeakLinkage) continue; IMPLGV->setLinkage(llvm::GlobalValue::ExternalLinkage); } for (unsigned i = 0; i < DefinedMetaClasses.size(); i++) { llvm::GlobalValue *IMPLGV = DefinedMetaClasses[i]; if (IMPLGV->getLinkage() != llvm::GlobalValue::ExternalWeakLinkage) continue; IMPLGV->setLinkage(llvm::GlobalValue::ExternalLinkage); } AddModuleClassList(DefinedNonLazyClasses, "\01L_OBJC_LABEL_NONLAZY_CLASS_$", "__DATA, __objc_nlclslist, regular, no_dead_strip"); // Build list of all implemented category addresses in array // L_OBJC_LABEL_CATEGORY_$. AddModuleClassList(DefinedCategories, "\01L_OBJC_LABEL_CATEGORY_$", "__DATA, __objc_catlist, regular, no_dead_strip"); AddModuleClassList(DefinedNonLazyCategories, "\01L_OBJC_LABEL_NONLAZY_CATEGORY_$", "__DATA, __objc_nlcatlist, regular, no_dead_strip"); EmitImageInfo(); } /// LegacyDispatchedSelector - Returns true if SEL is not in the list of /// NonLegacyDispatchMethods; false otherwise. What this means is that /// except for the 19 selectors in the list, we generate 32bit-style /// message dispatch call for all the rest. /// bool CGObjCNonFragileABIMac::LegacyDispatchedSelector(Selector Sel) { switch (CGM.getCodeGenOpts().getObjCDispatchMethod()) { default: assert(0 && "Invalid dispatch method!"); case CodeGenOptions::Legacy: return true; case CodeGenOptions::NonLegacy: return false; case CodeGenOptions::Mixed: break; } // If so, see whether this selector is in the white-list of things which must // use the new dispatch convention. We lazily build a dense set for this. if (NonLegacyDispatchMethods.empty()) { NonLegacyDispatchMethods.insert(GetNullarySelector("alloc")); NonLegacyDispatchMethods.insert(GetNullarySelector("class")); NonLegacyDispatchMethods.insert(GetNullarySelector("self")); NonLegacyDispatchMethods.insert(GetNullarySelector("isFlipped")); NonLegacyDispatchMethods.insert(GetNullarySelector("length")); NonLegacyDispatchMethods.insert(GetNullarySelector("count")); NonLegacyDispatchMethods.insert(GetNullarySelector("retain")); NonLegacyDispatchMethods.insert(GetNullarySelector("release")); NonLegacyDispatchMethods.insert(GetNullarySelector("autorelease")); NonLegacyDispatchMethods.insert(GetNullarySelector("hash")); NonLegacyDispatchMethods.insert(GetUnarySelector("allocWithZone")); NonLegacyDispatchMethods.insert(GetUnarySelector("isKindOfClass")); NonLegacyDispatchMethods.insert(GetUnarySelector("respondsToSelector")); NonLegacyDispatchMethods.insert(GetUnarySelector("objectForKey")); NonLegacyDispatchMethods.insert(GetUnarySelector("objectAtIndex")); NonLegacyDispatchMethods.insert(GetUnarySelector("isEqualToString")); NonLegacyDispatchMethods.insert(GetUnarySelector("isEqual")); NonLegacyDispatchMethods.insert(GetUnarySelector("addObject")); // "countByEnumeratingWithState:objects:count" IdentifierInfo *KeyIdents[] = { &CGM.getContext().Idents.get("countByEnumeratingWithState"), &CGM.getContext().Idents.get("objects"), &CGM.getContext().Idents.get("count") }; NonLegacyDispatchMethods.insert( CGM.getContext().Selectors.getSelector(3, KeyIdents)); } return (NonLegacyDispatchMethods.count(Sel) == 0); } // Metadata flags enum MetaDataDlags { CLS = 0x0, CLS_META = 0x1, CLS_ROOT = 0x2, OBJC2_CLS_HIDDEN = 0x10, CLS_EXCEPTION = 0x20 }; /// BuildClassRoTInitializer - generate meta-data for: /// struct _class_ro_t { /// uint32_t const flags; /// uint32_t const instanceStart; /// uint32_t const instanceSize; /// uint32_t const reserved; // only when building for 64bit targets /// const uint8_t * const ivarLayout; /// const char *const name; /// const struct _method_list_t * const baseMethods; /// const struct _protocol_list_t *const baseProtocols; /// const struct _ivar_list_t *const ivars; /// const uint8_t * const weakIvarLayout; /// const struct _prop_list_t * const properties; /// } /// llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer( unsigned flags, unsigned InstanceStart, unsigned InstanceSize, const ObjCImplementationDecl *ID) { std::string ClassName = ID->getNameAsString(); std::vector Values(10); // 11 for 64bit targets! Values[ 0] = llvm::ConstantInt::get(ObjCTypes.IntTy, flags); Values[ 1] = llvm::ConstantInt::get(ObjCTypes.IntTy, InstanceStart); Values[ 2] = llvm::ConstantInt::get(ObjCTypes.IntTy, InstanceSize); // FIXME. For 64bit targets add 0 here. Values[ 3] = (flags & CLS_META) ? GetIvarLayoutName(0, ObjCTypes) : BuildIvarLayout(ID, true); Values[ 4] = GetClassName(ID->getIdentifier()); // const struct _method_list_t * const baseMethods; std::vector Methods; std::string MethodListName("\01l_OBJC_$_"); if (flags & CLS_META) { MethodListName += "CLASS_METHODS_" + ID->getNameAsString(); for (ObjCImplementationDecl::classmeth_iterator i = ID->classmeth_begin(), e = ID->classmeth_end(); i != e; ++i) { // Class methods should always be defined. Methods.push_back(GetMethodConstant(*i)); } } else { MethodListName += "INSTANCE_METHODS_" + ID->getNameAsString(); for (ObjCImplementationDecl::instmeth_iterator i = ID->instmeth_begin(), e = ID->instmeth_end(); i != e; ++i) { // Instance methods should always be defined. Methods.push_back(GetMethodConstant(*i)); } for (ObjCImplementationDecl::propimpl_iterator i = ID->propimpl_begin(), e = ID->propimpl_end(); i != e; ++i) { ObjCPropertyImplDecl *PID = *i; if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize){ ObjCPropertyDecl *PD = PID->getPropertyDecl(); if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) if (llvm::Constant *C = GetMethodConstant(MD)) Methods.push_back(C); if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) if (llvm::Constant *C = GetMethodConstant(MD)) Methods.push_back(C); } } } Values[ 5] = EmitMethodList(MethodListName, "__DATA, __objc_const", Methods); const ObjCInterfaceDecl *OID = ID->getClassInterface(); assert(OID && "CGObjCNonFragileABIMac::BuildClassRoTInitializer"); Values[ 6] = EmitProtocolList("\01l_OBJC_CLASS_PROTOCOLS_$_" + OID->getName(), OID->protocol_begin(), OID->protocol_end()); if (flags & CLS_META) Values[ 7] = llvm::Constant::getNullValue(ObjCTypes.IvarListnfABIPtrTy); else Values[ 7] = EmitIvarList(ID); Values[ 8] = (flags & CLS_META) ? GetIvarLayoutName(0, ObjCTypes) : BuildIvarLayout(ID, false); if (flags & CLS_META) Values[ 9] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); else Values[ 9] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ID->getName(), ID, ID->getClassInterface(), ObjCTypes); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassRonfABITy, Values); llvm::GlobalVariable *CLASS_RO_GV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassRonfABITy, false, llvm::GlobalValue::InternalLinkage, Init, (flags & CLS_META) ? std::string("\01l_OBJC_METACLASS_RO_$_")+ClassName : std::string("\01l_OBJC_CLASS_RO_$_")+ClassName); CLASS_RO_GV->setAlignment( CGM.getTargetData().getABITypeAlignment(ObjCTypes.ClassRonfABITy)); CLASS_RO_GV->setSection("__DATA, __objc_const"); return CLASS_RO_GV; } /// BuildClassMetaData - This routine defines that to-level meta-data /// for the given ClassName for: /// struct _class_t { /// struct _class_t *isa; /// struct _class_t * const superclass; /// void *cache; /// IMP *vtable; /// struct class_ro_t *ro; /// } /// llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassMetaData( std::string &ClassName, llvm::Constant *IsAGV, llvm::Constant *SuperClassGV, llvm::Constant *ClassRoGV, bool HiddenVisibility) { std::vector Values(5); Values[0] = IsAGV; Values[1] = SuperClassGV; if (!Values[1]) Values[1] = llvm::Constant::getNullValue(ObjCTypes.ClassnfABIPtrTy); Values[2] = ObjCEmptyCacheVar; // &ObjCEmptyCacheVar Values[3] = ObjCEmptyVtableVar; // &ObjCEmptyVtableVar Values[4] = ClassRoGV; // &CLASS_RO_GV llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassnfABITy, Values); llvm::GlobalVariable *GV = GetClassGlobal(ClassName); GV->setInitializer(Init); GV->setSection("__DATA, __objc_data"); GV->setAlignment( CGM.getTargetData().getABITypeAlignment(ObjCTypes.ClassnfABITy)); if (HiddenVisibility) GV->setVisibility(llvm::GlobalValue::HiddenVisibility); return GV; } bool CGObjCNonFragileABIMac::ImplementationIsNonLazy(const ObjCImplDecl *OD) const { return OD->getClassMethod(GetNullarySelector("load")) != 0; } void CGObjCNonFragileABIMac::GetClassSizeInfo(const ObjCImplementationDecl *OID, uint32_t &InstanceStart, uint32_t &InstanceSize) { const ASTRecordLayout &RL = CGM.getContext().getASTObjCImplementationLayout(OID); // InstanceSize is really instance end. InstanceSize = llvm::RoundUpToAlignment(RL.getDataSize(), 8) / 8; // If there are no fields, the start is the same as the end. if (!RL.getFieldCount()) InstanceStart = InstanceSize; else InstanceStart = RL.getFieldOffset(0) / 8; } void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { std::string ClassName = ID->getNameAsString(); if (!ObjCEmptyCacheVar) { ObjCEmptyCacheVar = new llvm::GlobalVariable( CGM.getModule(), ObjCTypes.CacheTy, false, llvm::GlobalValue::ExternalLinkage, 0, "_objc_empty_cache"); ObjCEmptyVtableVar = new llvm::GlobalVariable( CGM.getModule(), ObjCTypes.ImpnfABITy, false, llvm::GlobalValue::ExternalLinkage, 0, "_objc_empty_vtable"); } assert(ID->getClassInterface() && "CGObjCNonFragileABIMac::GenerateClass - class is 0"); // FIXME: Is this correct (that meta class size is never computed)? uint32_t InstanceStart = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ClassnfABITy); uint32_t InstanceSize = InstanceStart; uint32_t flags = CLS_META; std::string ObjCMetaClassName(getMetaclassSymbolPrefix()); std::string ObjCClassName(getClassSymbolPrefix()); llvm::GlobalVariable *SuperClassGV, *IsAGV; bool classIsHidden = CGM.getDeclVisibilityMode(ID->getClassInterface()) == LangOptions::Hidden; if (classIsHidden) flags |= OBJC2_CLS_HIDDEN; if (ID->getNumIvarInitializers()) flags |= eClassFlags_ABI2_HasCXXStructors; if (!ID->getClassInterface()->getSuperClass()) { // class is root flags |= CLS_ROOT; SuperClassGV = GetClassGlobal(ObjCClassName + ClassName); IsAGV = GetClassGlobal(ObjCMetaClassName + ClassName); } else { // Has a root. Current class is not a root. const ObjCInterfaceDecl *Root = ID->getClassInterface(); while (const ObjCInterfaceDecl *Super = Root->getSuperClass()) Root = Super; IsAGV = GetClassGlobal(ObjCMetaClassName + Root->getNameAsString()); if (Root->hasAttr()) IsAGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); // work on super class metadata symbol. std::string SuperClassName = ObjCMetaClassName + ID->getClassInterface()->getSuperClass()->getNameAsString(); SuperClassGV = GetClassGlobal(SuperClassName); if (ID->getClassInterface()->getSuperClass()->hasAttr()) SuperClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } llvm::GlobalVariable *CLASS_RO_GV = BuildClassRoTInitializer(flags, InstanceStart, InstanceSize,ID); std::string TClassName = ObjCMetaClassName + ClassName; llvm::GlobalVariable *MetaTClass = BuildClassMetaData(TClassName, IsAGV, SuperClassGV, CLASS_RO_GV, classIsHidden); DefinedMetaClasses.push_back(MetaTClass); // Metadata for the class flags = CLS; if (classIsHidden) flags |= OBJC2_CLS_HIDDEN; if (ID->getNumIvarInitializers()) flags |= eClassFlags_ABI2_HasCXXStructors; if (hasObjCExceptionAttribute(CGM.getContext(), ID->getClassInterface())) flags |= CLS_EXCEPTION; if (!ID->getClassInterface()->getSuperClass()) { flags |= CLS_ROOT; SuperClassGV = 0; } else { // Has a root. Current class is not a root. std::string RootClassName = ID->getClassInterface()->getSuperClass()->getNameAsString(); SuperClassGV = GetClassGlobal(ObjCClassName + RootClassName); if (ID->getClassInterface()->getSuperClass()->hasAttr()) SuperClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } GetClassSizeInfo(ID, InstanceStart, InstanceSize); CLASS_RO_GV = BuildClassRoTInitializer(flags, InstanceStart, InstanceSize, ID); TClassName = ObjCClassName + ClassName; llvm::GlobalVariable *ClassMD = BuildClassMetaData(TClassName, MetaTClass, SuperClassGV, CLASS_RO_GV, classIsHidden); DefinedClasses.push_back(ClassMD); // Determine if this class is also "non-lazy". if (ImplementationIsNonLazy(ID)) DefinedNonLazyClasses.push_back(ClassMD); // Force the definition of the EHType if necessary. if (flags & CLS_EXCEPTION) GetInterfaceEHType(ID->getClassInterface(), true); } /// GenerateProtocolRef - This routine is called to generate code for /// a protocol reference expression; as in: /// @code /// @protocol(Proto1); /// @endcode /// It generates a weak reference to l_OBJC_PROTOCOL_REFERENCE_$_Proto1 /// which will hold address of the protocol meta-data. /// llvm::Value *CGObjCNonFragileABIMac::GenerateProtocolRef(CGBuilderTy &Builder, const ObjCProtocolDecl *PD) { // This routine is called for @protocol only. So, we must build definition // of protocol's meta-data (not a reference to it!) // llvm::Constant *Init = llvm::ConstantExpr::getBitCast(GetOrEmitProtocol(PD), ObjCTypes.ExternalProtocolPtrTy); std::string ProtocolName("\01l_OBJC_PROTOCOL_REFERENCE_$_"); ProtocolName += PD->getNameAsCString(); llvm::GlobalVariable *PTGV = CGM.getModule().getGlobalVariable(ProtocolName); if (PTGV) return Builder.CreateLoad(PTGV, "tmp"); PTGV = new llvm::GlobalVariable( CGM.getModule(), Init->getType(), false, llvm::GlobalValue::WeakAnyLinkage, Init, ProtocolName); PTGV->setSection("__DATA, __objc_protorefs, coalesced, no_dead_strip"); PTGV->setVisibility(llvm::GlobalValue::HiddenVisibility); CGM.AddUsedGlobal(PTGV); return Builder.CreateLoad(PTGV, "tmp"); } /// GenerateCategory - Build metadata for a category implementation. /// struct _category_t { /// const char * const name; /// struct _class_t *const cls; /// const struct _method_list_t * const instance_methods; /// const struct _method_list_t * const class_methods; /// const struct _protocol_list_t * const protocols; /// const struct _prop_list_t * const properties; /// } /// void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { const ObjCInterfaceDecl *Interface = OCD->getClassInterface(); const char *Prefix = "\01l_OBJC_$_CATEGORY_"; std::string ExtCatName(Prefix + Interface->getNameAsString()+ "_$_" + OCD->getNameAsString()); std::string ExtClassName(getClassSymbolPrefix() + Interface->getNameAsString()); std::vector Values(6); Values[0] = GetClassName(OCD->getIdentifier()); // meta-class entry symbol llvm::GlobalVariable *ClassGV = GetClassGlobal(ExtClassName); if (Interface->hasAttr()) ClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); Values[1] = ClassGV; std::vector Methods; std::string MethodListName(Prefix); MethodListName += "INSTANCE_METHODS_" + Interface->getNameAsString() + "_$_" + OCD->getNameAsString(); for (ObjCCategoryImplDecl::instmeth_iterator i = OCD->instmeth_begin(), e = OCD->instmeth_end(); i != e; ++i) { // Instance methods should always be defined. Methods.push_back(GetMethodConstant(*i)); } Values[2] = EmitMethodList(MethodListName, "__DATA, __objc_const", Methods); MethodListName = Prefix; MethodListName += "CLASS_METHODS_" + Interface->getNameAsString() + "_$_" + OCD->getNameAsString(); Methods.clear(); for (ObjCCategoryImplDecl::classmeth_iterator i = OCD->classmeth_begin(), e = OCD->classmeth_end(); i != e; ++i) { // Class methods should always be defined. Methods.push_back(GetMethodConstant(*i)); } Values[3] = EmitMethodList(MethodListName, "__DATA, __objc_const", Methods); const ObjCCategoryDecl *Category = Interface->FindCategoryDeclaration(OCD->getIdentifier()); if (Category) { llvm::SmallString<256> ExtName; llvm::raw_svector_ostream(ExtName) << Interface->getName() << "_$_" << OCD->getName(); Values[4] = EmitProtocolList("\01l_OBJC_CATEGORY_PROTOCOLS_$_" + Interface->getName() + "_$_" + Category->getName(), Category->protocol_begin(), Category->protocol_end()); Values[5] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ExtName.str(), OCD, Category, ObjCTypes); } else { Values[4] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy); Values[5] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); } llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.CategorynfABITy, Values); llvm::GlobalVariable *GCATV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.CategorynfABITy, false, llvm::GlobalValue::InternalLinkage, Init, ExtCatName); GCATV->setAlignment( CGM.getTargetData().getABITypeAlignment(ObjCTypes.CategorynfABITy)); GCATV->setSection("__DATA, __objc_const"); CGM.AddUsedGlobal(GCATV); DefinedCategories.push_back(GCATV); // Determine if this category is also "non-lazy". if (ImplementationIsNonLazy(OCD)) DefinedNonLazyCategories.push_back(GCATV); } /// GetMethodConstant - Return a struct objc_method constant for the /// given method if it has been defined. The result is null if the /// method has not been defined. The return value has type MethodPtrTy. llvm::Constant *CGObjCNonFragileABIMac::GetMethodConstant( const ObjCMethodDecl *MD) { // FIXME: Use DenseMap::lookup llvm::Function *Fn = MethodDefinitions[MD]; if (!Fn) return 0; std::vector Method(3); Method[0] = llvm::ConstantExpr::getBitCast(GetMethodVarName(MD->getSelector()), ObjCTypes.SelectorPtrTy); Method[1] = GetMethodVarType(MD); Method[2] = llvm::ConstantExpr::getBitCast(Fn, ObjCTypes.Int8PtrTy); return llvm::ConstantStruct::get(ObjCTypes.MethodTy, Method); } /// EmitMethodList - Build meta-data for method declarations /// struct _method_list_t { /// uint32_t entsize; // sizeof(struct _objc_method) /// uint32_t method_count; /// struct _objc_method method_list[method_count]; /// } /// llvm::Constant *CGObjCNonFragileABIMac::EmitMethodList(llvm::Twine Name, const char *Section, const ConstantVector &Methods) { // Return null for empty list. if (Methods.empty()) return llvm::Constant::getNullValue(ObjCTypes.MethodListnfABIPtrTy); std::vector Values(3); // sizeof(struct _objc_method) unsigned Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.MethodTy); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); // method_count Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Methods.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.MethodTy, Methods.size()); Values[2] = llvm::ConstantArray::get(AT, Methods); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, llvm::GlobalValue::InternalLinkage, Init, Name); GV->setAlignment( CGM.getTargetData().getABITypeAlignment(Init->getType())); GV->setSection(Section); CGM.AddUsedGlobal(GV); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.MethodListnfABIPtrTy); } /// ObjCIvarOffsetVariable - Returns the ivar offset variable for /// the given ivar. llvm::GlobalVariable * CGObjCNonFragileABIMac::ObjCIvarOffsetVariable(const ObjCInterfaceDecl *ID, const ObjCIvarDecl *Ivar) { const ObjCInterfaceDecl *Container = Ivar->getContainingInterface(); std::string Name = "OBJC_IVAR_$_" + Container->getNameAsString() + '.' + Ivar->getNameAsString(); llvm::GlobalVariable *IvarOffsetGV = CGM.getModule().getGlobalVariable(Name); if (!IvarOffsetGV) IvarOffsetGV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.LongTy, false, llvm::GlobalValue::ExternalLinkage, 0, Name); return IvarOffsetGV; } llvm::Constant * CGObjCNonFragileABIMac::EmitIvarOffsetVar(const ObjCInterfaceDecl *ID, const ObjCIvarDecl *Ivar, unsigned long int Offset) { llvm::GlobalVariable *IvarOffsetGV = ObjCIvarOffsetVariable(ID, Ivar); IvarOffsetGV->setInitializer(llvm::ConstantInt::get(ObjCTypes.LongTy, Offset)); IvarOffsetGV->setAlignment( CGM.getTargetData().getABITypeAlignment(ObjCTypes.LongTy)); // FIXME: This matches gcc, but shouldn't the visibility be set on the use as // well (i.e., in ObjCIvarOffsetVariable). if (Ivar->getAccessControl() == ObjCIvarDecl::Private || Ivar->getAccessControl() == ObjCIvarDecl::Package || CGM.getDeclVisibilityMode(ID) == LangOptions::Hidden) IvarOffsetGV->setVisibility(llvm::GlobalValue::HiddenVisibility); else IvarOffsetGV->setVisibility(llvm::GlobalValue::DefaultVisibility); IvarOffsetGV->setSection("__DATA, __objc_const"); return IvarOffsetGV; } /// EmitIvarList - Emit the ivar list for the given /// implementation. The return value has type /// IvarListnfABIPtrTy. /// struct _ivar_t { /// unsigned long int *offset; // pointer to ivar offset location /// char *name; /// char *type; /// uint32_t alignment; /// uint32_t size; /// } /// struct _ivar_list_t { /// uint32 entsize; // sizeof(struct _ivar_t) /// uint32 count; /// struct _iver_t list[count]; /// } /// llvm::Constant *CGObjCNonFragileABIMac::EmitIvarList( const ObjCImplementationDecl *ID) { std::vector Ivars, Ivar(5); const ObjCInterfaceDecl *OID = ID->getClassInterface(); assert(OID && "CGObjCNonFragileABIMac::EmitIvarList - null interface"); // FIXME. Consolidate this with similar code in GenerateClass. // Collect declared and synthesized ivars in a small vector. llvm::SmallVector OIvars; CGM.getContext().ShallowCollectObjCIvars(OID, OIvars); for (unsigned i = 0, e = OIvars.size(); i != e; ++i) { ObjCIvarDecl *IVD = OIvars[i]; // Ignore unnamed bit-fields. if (!IVD->getDeclName()) continue; Ivar[0] = EmitIvarOffsetVar(ID->getClassInterface(), IVD, ComputeIvarBaseOffset(CGM, ID, IVD)); Ivar[1] = GetMethodVarName(IVD->getIdentifier()); Ivar[2] = GetMethodVarType(IVD); const llvm::Type *FieldTy = CGM.getTypes().ConvertTypeForMem(IVD->getType()); unsigned Size = CGM.getTargetData().getTypeAllocSize(FieldTy); unsigned Align = CGM.getContext().getPreferredTypeAlign( IVD->getType().getTypePtr()) >> 3; Align = llvm::Log2_32(Align); Ivar[3] = llvm::ConstantInt::get(ObjCTypes.IntTy, Align); // NOTE. Size of a bitfield does not match gcc's, because of the // way bitfields are treated special in each. But I am told that // 'size' for bitfield ivars is ignored by the runtime so it does // not matter. If it matters, there is enough info to get the // bitfield right! Ivar[4] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Ivars.push_back(llvm::ConstantStruct::get(ObjCTypes.IvarnfABITy, Ivar)); } // Return null for empty list. if (Ivars.empty()) return llvm::Constant::getNullValue(ObjCTypes.IvarListnfABIPtrTy); std::vector Values(3); unsigned Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.IvarnfABITy); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Ivars.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.IvarnfABITy, Ivars.size()); Values[2] = llvm::ConstantArray::get(AT, Ivars); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); const char *Prefix = "\01l_OBJC_$_INSTANCE_VARIABLES_"; llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, llvm::GlobalValue::InternalLinkage, Init, Prefix + OID->getName()); GV->setAlignment( CGM.getTargetData().getABITypeAlignment(Init->getType())); GV->setSection("__DATA, __objc_const"); CGM.AddUsedGlobal(GV); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.IvarListnfABIPtrTy); } llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocolRef( const ObjCProtocolDecl *PD) { llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()]; if (!Entry) { // We use the initializer as a marker of whether this is a forward // reference or not. At module finalization we add the empty // contents for protocols which were referenced but never defined. Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolnfABITy, false, llvm::GlobalValue::ExternalLinkage, 0, "\01l_OBJC_PROTOCOL_$_" + PD->getName()); Entry->setSection("__DATA,__datacoal_nt,coalesced"); } return Entry; } /// GetOrEmitProtocol - Generate the protocol meta-data: /// @code /// struct _protocol_t { /// id isa; // NULL /// const char * const protocol_name; /// const struct _protocol_list_t * protocol_list; // super protocols /// const struct method_list_t * const instance_methods; /// const struct method_list_t * const class_methods; /// const struct method_list_t *optionalInstanceMethods; /// const struct method_list_t *optionalClassMethods; /// const struct _prop_list_t * properties; /// const uint32_t size; // sizeof(struct _protocol_t) /// const uint32_t flags; // = 0 /// } /// @endcode /// llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol( const ObjCProtocolDecl *PD) { llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()]; // Early exit if a defining object has already been generated. if (Entry && Entry->hasInitializer()) return Entry; // Construct method lists. std::vector InstanceMethods, ClassMethods; std::vector OptInstanceMethods, OptClassMethods; for (ObjCProtocolDecl::instmeth_iterator i = PD->instmeth_begin(), e = PD->instmeth_end(); i != e; ++i) { ObjCMethodDecl *MD = *i; llvm::Constant *C = GetMethodDescriptionConstant(MD); if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { OptInstanceMethods.push_back(C); } else { InstanceMethods.push_back(C); } } for (ObjCProtocolDecl::classmeth_iterator i = PD->classmeth_begin(), e = PD->classmeth_end(); i != e; ++i) { ObjCMethodDecl *MD = *i; llvm::Constant *C = GetMethodDescriptionConstant(MD); if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { OptClassMethods.push_back(C); } else { ClassMethods.push_back(C); } } std::vector Values(10); // isa is NULL Values[0] = llvm::Constant::getNullValue(ObjCTypes.ObjectPtrTy); Values[1] = GetClassName(PD->getIdentifier()); Values[2] = EmitProtocolList("\01l_OBJC_$_PROTOCOL_REFS_" + PD->getName(), PD->protocol_begin(), PD->protocol_end()); Values[3] = EmitMethodList("\01l_OBJC_$_PROTOCOL_INSTANCE_METHODS_" + PD->getName(), "__DATA, __objc_const", InstanceMethods); Values[4] = EmitMethodList("\01l_OBJC_$_PROTOCOL_CLASS_METHODS_" + PD->getName(), "__DATA, __objc_const", ClassMethods); Values[5] = EmitMethodList("\01l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_" + PD->getName(), "__DATA, __objc_const", OptInstanceMethods); Values[6] = EmitMethodList("\01l_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_" + PD->getName(), "__DATA, __objc_const", OptClassMethods); Values[7] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + PD->getName(), 0, PD, ObjCTypes); uint32_t Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ProtocolnfABITy); Values[8] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Values[9] = llvm::Constant::getNullValue(ObjCTypes.IntTy); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ProtocolnfABITy, Values); if (Entry) { // Already created, fix the linkage and update the initializer. Entry->setLinkage(llvm::GlobalValue::WeakAnyLinkage); Entry->setInitializer(Init); } else { Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolnfABITy, false, llvm::GlobalValue::WeakAnyLinkage, Init, "\01l_OBJC_PROTOCOL_$_" + PD->getName()); Entry->setAlignment( CGM.getTargetData().getABITypeAlignment(ObjCTypes.ProtocolnfABITy)); Entry->setSection("__DATA,__datacoal_nt,coalesced"); } Entry->setVisibility(llvm::GlobalValue::HiddenVisibility); CGM.AddUsedGlobal(Entry); // Use this protocol meta-data to build protocol list table in section // __DATA, __objc_protolist llvm::GlobalVariable *PTGV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolnfABIPtrTy, false, llvm::GlobalValue::WeakAnyLinkage, Entry, "\01l_OBJC_LABEL_PROTOCOL_$_" + PD->getName()); PTGV->setAlignment( CGM.getTargetData().getABITypeAlignment(ObjCTypes.ProtocolnfABIPtrTy)); PTGV->setSection("__DATA, __objc_protolist, coalesced, no_dead_strip"); PTGV->setVisibility(llvm::GlobalValue::HiddenVisibility); CGM.AddUsedGlobal(PTGV); return Entry; } /// EmitProtocolList - Generate protocol list meta-data: /// @code /// struct _protocol_list_t { /// long protocol_count; // Note, this is 32/64 bit /// struct _protocol_t[protocol_count]; /// } /// @endcode /// llvm::Constant * CGObjCNonFragileABIMac::EmitProtocolList(llvm::Twine Name, ObjCProtocolDecl::protocol_iterator begin, ObjCProtocolDecl::protocol_iterator end) { std::vector ProtocolRefs; // Just return null for empty protocol lists if (begin == end) return llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy); // FIXME: We shouldn't need to do this lookup here, should we? llvm::SmallString<256> TmpName; Name.toVector(TmpName); llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(TmpName.str(), true); if (GV) return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ProtocolListnfABIPtrTy); for (; begin != end; ++begin) ProtocolRefs.push_back(GetProtocolRef(*begin)); // Implemented??? // This list is null terminated. ProtocolRefs.push_back(llvm::Constant::getNullValue( ObjCTypes.ProtocolnfABIPtrTy)); std::vector Values(2); Values[0] = llvm::ConstantInt::get(ObjCTypes.LongTy, ProtocolRefs.size() - 1); Values[1] = llvm::ConstantArray::get( llvm::ArrayType::get(ObjCTypes.ProtocolnfABIPtrTy, ProtocolRefs.size()), ProtocolRefs); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, llvm::GlobalValue::InternalLinkage, Init, Name); GV->setSection("__DATA, __objc_const"); GV->setAlignment( CGM.getTargetData().getABITypeAlignment(Init->getType())); CGM.AddUsedGlobal(GV); return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ProtocolListnfABIPtrTy); } /// GetMethodDescriptionConstant - This routine build following meta-data: /// struct _objc_method { /// SEL _cmd; /// char *method_type; /// char *_imp; /// } llvm::Constant * CGObjCNonFragileABIMac::GetMethodDescriptionConstant(const ObjCMethodDecl *MD) { std::vector Desc(3); Desc[0] = llvm::ConstantExpr::getBitCast(GetMethodVarName(MD->getSelector()), ObjCTypes.SelectorPtrTy); Desc[1] = GetMethodVarType(MD); // Protocol methods have no implementation. So, this entry is always NULL. Desc[2] = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); return llvm::ConstantStruct::get(ObjCTypes.MethodTy, Desc); } /// EmitObjCValueForIvar - Code Gen for nonfragile ivar reference. /// This code gen. amounts to generating code for: /// @code /// (type *)((char *)base + _OBJC_IVAR_$_.ivar; /// @encode /// LValue CGObjCNonFragileABIMac::EmitObjCValueForIvar( CodeGen::CodeGenFunction &CGF, QualType ObjectTy, llvm::Value *BaseValue, const ObjCIvarDecl *Ivar, unsigned CVRQualifiers) { ObjCInterfaceDecl *ID = ObjectTy->getAs()->getInterface(); return EmitValueForIvarAtOffset(CGF, ID, BaseValue, Ivar, CVRQualifiers, EmitIvarOffset(CGF, ID, Ivar)); } llvm::Value *CGObjCNonFragileABIMac::EmitIvarOffset( CodeGen::CodeGenFunction &CGF, const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar) { return CGF.Builder.CreateLoad(ObjCIvarOffsetVariable(Interface, Ivar),"ivar"); } CodeGen::RValue CGObjCNonFragileABIMac::EmitMessageSend( CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Receiver, QualType Arg0Ty, bool IsSuper, const CallArgList &CallArgs) { // FIXME. Even though IsSuper is passes. This function doese not handle calls // to 'super' receivers. CodeGenTypes &Types = CGM.getTypes(); llvm::Value *Arg0 = Receiver; if (!IsSuper) Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy, "tmp"); // Find the message function name. // FIXME. This is too much work to get the ABI-specific result type needed to // find the message name. const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, CallArgList(), FunctionType::ExtInfo()); llvm::Constant *Fn = 0; std::string Name("\01l_"); if (CGM.ReturnTypeUsesSret(FnInfo)) { #if 0 // unlike what is documented. gcc never generates this API!! if (Receiver->getType() == ObjCTypes.ObjectPtrTy) { Fn = ObjCTypes.getMessageSendIdStretFixupFn(); // FIXME. Is there a better way of getting these names. // They are available in RuntimeFunctions vector pair. Name += "objc_msgSendId_stret_fixup"; } else #endif if (IsSuper) { Fn = ObjCTypes.getMessageSendSuper2StretFixupFn(); Name += "objc_msgSendSuper2_stret_fixup"; } else { Fn = ObjCTypes.getMessageSendStretFixupFn(); Name += "objc_msgSend_stret_fixup"; } } else if (!IsSuper && ResultType->isFloatingType()) { if (ResultType->isSpecificBuiltinType(BuiltinType::LongDouble)) { Fn = ObjCTypes.getMessageSendFpretFixupFn(); Name += "objc_msgSend_fpret_fixup"; } else { Fn = ObjCTypes.getMessageSendFixupFn(); Name += "objc_msgSend_fixup"; } } else { #if 0 // unlike what is documented. gcc never generates this API!! if (Receiver->getType() == ObjCTypes.ObjectPtrTy) { Fn = ObjCTypes.getMessageSendIdFixupFn(); Name += "objc_msgSendId_fixup"; } else #endif if (IsSuper) { Fn = ObjCTypes.getMessageSendSuper2FixupFn(); Name += "objc_msgSendSuper2_fixup"; } else { Fn = ObjCTypes.getMessageSendFixupFn(); Name += "objc_msgSend_fixup"; } } assert(Fn && "CGObjCNonFragileABIMac::EmitMessageSend"); Name += '_'; std::string SelName(Sel.getAsString()); // Replace all ':' in selector name with '_' ouch! for (unsigned i = 0; i < SelName.size(); i++) if (SelName[i] == ':') SelName[i] = '_'; Name += SelName; llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name); if (!GV) { // Build message ref table entry. std::vector Values(2); Values[0] = Fn; Values[1] = GetMethodVarName(Sel); llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, llvm::GlobalValue::WeakAnyLinkage, Init, Name); GV->setVisibility(llvm::GlobalValue::HiddenVisibility); GV->setAlignment(16); GV->setSection("__DATA, __objc_msgrefs, coalesced"); } llvm::Value *Arg1 = CGF.Builder.CreateBitCast(GV, ObjCTypes.MessageRefPtrTy); CallArgList ActualArgs; ActualArgs.push_back(std::make_pair(RValue::get(Arg0), Arg0Ty)); ActualArgs.push_back(std::make_pair(RValue::get(Arg1), ObjCTypes.MessageRefCPtrTy)); ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); const CGFunctionInfo &FnInfo1 = Types.getFunctionInfo(ResultType, ActualArgs, FunctionType::ExtInfo()); llvm::Value *Callee = CGF.Builder.CreateStructGEP(Arg1, 0); Callee = CGF.Builder.CreateLoad(Callee); const llvm::FunctionType *FTy = Types.GetFunctionType(FnInfo1, true); Callee = CGF.Builder.CreateBitCast(Callee, llvm::PointerType::getUnqual(FTy)); return CGF.EmitCall(FnInfo1, Callee, Return, ActualArgs); } /// Generate code for a message send expression in the nonfragile abi. CodeGen::RValue CGObjCNonFragileABIMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, llvm::Value *Receiver, const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method) { return LegacyDispatchedSelector(Sel) ? EmitLegacyMessageSend(CGF, Return, ResultType, EmitSelector(CGF.Builder, Sel), Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method, ObjCTypes) : EmitMessageSend(CGF, Return, ResultType, Sel, Receiver, CGF.getContext().getObjCIdType(), false, CallArgs); } llvm::GlobalVariable * CGObjCNonFragileABIMac::GetClassGlobal(const std::string &Name) { llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name); if (!GV) { GV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABITy, false, llvm::GlobalValue::ExternalLinkage, 0, Name); } return GV; } llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { llvm::GlobalVariable *&Entry = ClassReferences[ID->getIdentifier()]; if (!Entry) { std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::InternalLinkage, ClassGV, "\01L_OBJC_CLASSLIST_REFERENCES_$_"); Entry->setAlignment( CGM.getTargetData().getABITypeAlignment( ObjCTypes.ClassnfABIPtrTy)); Entry->setSection("__DATA, __objc_classrefs, regular, no_dead_strip"); CGM.AddUsedGlobal(Entry); } return Builder.CreateLoad(Entry, "tmp"); } llvm::Value * CGObjCNonFragileABIMac::EmitSuperClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { llvm::GlobalVariable *&Entry = SuperClassReferences[ID->getIdentifier()]; if (!Entry) { std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::InternalLinkage, ClassGV, "\01L_OBJC_CLASSLIST_SUP_REFS_$_"); Entry->setAlignment( CGM.getTargetData().getABITypeAlignment( ObjCTypes.ClassnfABIPtrTy)); Entry->setSection("__DATA, __objc_superrefs, regular, no_dead_strip"); CGM.AddUsedGlobal(Entry); } return Builder.CreateLoad(Entry, "tmp"); } /// EmitMetaClassRef - Return a Value * of the address of _class_t /// meta-data /// llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { llvm::GlobalVariable * &Entry = MetaClassReferences[ID->getIdentifier()]; if (Entry) return Builder.CreateLoad(Entry, "tmp"); std::string MetaClassName(getMetaclassSymbolPrefix() + ID->getNameAsString()); llvm::GlobalVariable *MetaClassGV = GetClassGlobal(MetaClassName); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::InternalLinkage, MetaClassGV, "\01L_OBJC_CLASSLIST_SUP_REFS_$_"); Entry->setAlignment( CGM.getTargetData().getABITypeAlignment( ObjCTypes.ClassnfABIPtrTy)); Entry->setSection("__DATA, __objc_superrefs, regular, no_dead_strip"); CGM.AddUsedGlobal(Entry); return Builder.CreateLoad(Entry, "tmp"); } /// GetClass - Return a reference to the class for the given interface /// decl. llvm::Value *CGObjCNonFragileABIMac::GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { if (ID->hasAttr()) { std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName); ClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } return EmitClassRef(Builder, ID); } /// 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. CodeGen::RValue CGObjCNonFragileABIMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, Selector Sel, const ObjCInterfaceDecl *Class, bool isCategoryImpl, llvm::Value *Receiver, bool IsClassMessage, const CodeGen::CallArgList &CallArgs, const ObjCMethodDecl *Method) { // ... // Create and init a super structure; this is a (receiver, class) // pair we will pass to objc_msgSendSuper. llvm::Value *ObjCSuper = CGF.Builder.CreateAlloca(ObjCTypes.SuperTy, 0, "objc_super"); llvm::Value *ReceiverAsObject = CGF.Builder.CreateBitCast(Receiver, ObjCTypes.ObjectPtrTy); CGF.Builder.CreateStore(ReceiverAsObject, CGF.Builder.CreateStructGEP(ObjCSuper, 0)); // If this is a class message the metaclass is passed as the target. llvm::Value *Target; if (IsClassMessage) { if (isCategoryImpl) { // Message sent to "super' in a class method defined in // a category implementation. Target = EmitClassRef(CGF.Builder, Class); Target = CGF.Builder.CreateStructGEP(Target, 0); Target = CGF.Builder.CreateLoad(Target); } else Target = EmitMetaClassRef(CGF.Builder, Class); } else Target = EmitSuperClassRef(CGF.Builder, Class); // FIXME: We shouldn't need to do this cast, rectify the ASTContext and // ObjCTypes types. const llvm::Type *ClassTy = CGM.getTypes().ConvertType(CGF.getContext().getObjCClassType()); Target = CGF.Builder.CreateBitCast(Target, ClassTy); CGF.Builder.CreateStore(Target, CGF.Builder.CreateStructGEP(ObjCSuper, 1)); return (LegacyDispatchedSelector(Sel)) ? EmitLegacyMessageSend(CGF, Return, ResultType, EmitSelector(CGF.Builder, Sel), ObjCSuper, ObjCTypes.SuperPtrCTy, true, CallArgs, Method, ObjCTypes) : EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper, ObjCTypes.SuperPtrCTy, true, CallArgs); } llvm::Value *CGObjCNonFragileABIMac::EmitSelector(CGBuilderTy &Builder, Selector Sel) { llvm::GlobalVariable *&Entry = SelectorReferences[Sel]; if (!Entry) { llvm::Constant *Casted = llvm::ConstantExpr::getBitCast(GetMethodVarName(Sel), ObjCTypes.SelectorPtrTy); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.SelectorPtrTy, false, llvm::GlobalValue::InternalLinkage, Casted, "\01L_OBJC_SELECTOR_REFERENCES_"); Entry->setSection("__DATA, __objc_selrefs, literal_pointers, no_dead_strip"); CGM.AddUsedGlobal(Entry); } return Builder.CreateLoad(Entry, "tmp"); } /// EmitObjCIvarAssign - Code gen for assigning to a __strong object. /// objc_assign_ivar (id src, id *dst, ptrdiff_t) /// void CGObjCNonFragileABIMac::EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst, llvm::Value *ivarOffset) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4 ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongTy)); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall3(ObjCTypes.getGcAssignIvarFn(), src, dst, ivarOffset); return; } /// EmitObjCStrongCastAssign - Code gen for assigning to a __strong cast object. /// objc_assign_strongCast (id src, id *dst) /// void CGObjCNonFragileABIMac::EmitObjCStrongCastAssign( CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4 ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongTy)); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall2(ObjCTypes.getGcAssignStrongCastFn(), src, dst, "weakassign"); return; } void CGObjCNonFragileABIMac::EmitGCMemmoveCollectable( CodeGen::CodeGenFunction &CGF, llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType Ty) { // Get size info for this aggregate. std::pair TypeInfo = CGM.getContext().getTypeInfo(Ty); unsigned long size = TypeInfo.first/8; SrcPtr = CGF.Builder.CreateBitCast(SrcPtr, ObjCTypes.Int8PtrTy); DestPtr = CGF.Builder.CreateBitCast(DestPtr, ObjCTypes.Int8PtrTy); llvm::Value *N = llvm::ConstantInt::get(ObjCTypes.LongTy, size); CGF.Builder.CreateCall3(ObjCTypes.GcMemmoveCollectableFn(), DestPtr, SrcPtr, N); return; } /// EmitObjCWeakRead - Code gen for loading value of a __weak /// object: objc_read_weak (id *src) /// llvm::Value * CGObjCNonFragileABIMac::EmitObjCWeakRead( CodeGen::CodeGenFunction &CGF, llvm::Value *AddrWeakObj) { const llvm::Type* DestTy = cast(AddrWeakObj->getType())->getElementType(); AddrWeakObj = CGF.Builder.CreateBitCast(AddrWeakObj, ObjCTypes.PtrObjectPtrTy); llvm::Value *read_weak = CGF.Builder.CreateCall(ObjCTypes.getGcReadWeakFn(), AddrWeakObj, "weakread"); read_weak = CGF.Builder.CreateBitCast(read_weak, DestTy); return read_weak; } /// EmitObjCWeakAssign - Code gen for assigning to a __weak object. /// objc_assign_weak (id src, id *dst) /// void CGObjCNonFragileABIMac::EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4 ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongTy)); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall2(ObjCTypes.getGcAssignWeakFn(), src, dst, "weakassign"); return; } /// EmitObjCGlobalAssign - Code gen for assigning to a __strong object. /// objc_assign_global (id src, id *dst) /// void CGObjCNonFragileABIMac::EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF, llvm::Value *src, llvm::Value *dst) { const llvm::Type * SrcTy = src->getType(); if (!isa(SrcTy)) { unsigned Size = CGM.getTargetData().getTypeAllocSize(SrcTy); assert(Size <= 8 && "does not support size > 8"); src = (Size == 4 ? CGF.Builder.CreateBitCast(src, ObjCTypes.IntTy) : CGF.Builder.CreateBitCast(src, ObjCTypes.LongTy)); src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy); } src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy); dst = CGF.Builder.CreateBitCast(dst, ObjCTypes.PtrObjectPtrTy); CGF.Builder.CreateCall2(ObjCTypes.getGcAssignGlobalFn(), src, dst, "globalassign"); return; } void CGObjCNonFragileABIMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S) { bool isTry = isa(S); llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try"); llvm::BasicBlock *PrevLandingPad = CGF.getInvokeDest(); llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler"); llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally"); llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw"); llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end"); // For @synchronized, call objc_sync_enter(sync.expr). The // evaluation of the expression must occur before we enter the // @synchronized. We can safely avoid a temp here because jumps into // @synchronized are illegal & this will dominate uses. llvm::Value *SyncArg = 0; if (!isTry) { SyncArg = CGF.EmitScalarExpr(cast(S).getSynchExpr()); SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy); CGF.Builder.CreateCall(ObjCTypes.getSyncEnterFn(), SyncArg); } // Push an EH context entry, used for handling rethrows and jumps // through finally. CGF.PushCleanupBlock(FinallyBlock); CGF.setInvokeDest(TryHandler); CGF.EmitBlock(TryBlock); CGF.EmitStmt(isTry ? cast(S).getTryBody() : cast(S).getSynchBody()); CGF.EmitBranchThroughCleanup(FinallyEnd); // Emit the exception handler. CGF.EmitBlock(TryHandler); llvm::Value *llvm_eh_exception = CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_exception); llvm::Value *llvm_eh_selector = CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_selector); llvm::Value *llvm_eh_typeid_for = CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for); llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); llvm::Value *RethrowPtr = CGF.CreateTempAlloca(Exc->getType(), "_rethrow"); llvm::SmallVector SelectorArgs; SelectorArgs.push_back(Exc); SelectorArgs.push_back(ObjCTypes.getEHPersonalityPtr()); // Construct the lists of (type, catch body) to handle. llvm::SmallVector, 8> Handlers; bool HasCatchAll = false; if (isTry) { const ObjCAtTryStmt &AtTry = cast(S); for (unsigned I = 0, N = AtTry.getNumCatchStmts(); I != N; ++I) { const ObjCAtCatchStmt *CatchStmt = AtTry.getCatchStmt(I); const VarDecl *CatchDecl = CatchStmt->getCatchParamDecl(); Handlers.push_back(std::make_pair(CatchDecl, CatchStmt->getCatchBody())); // catch(...) always matches. if (!CatchDecl) { // Use i8* null here to signal this is a catch all, not a cleanup. llvm::Value *Null = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); SelectorArgs.push_back(Null); HasCatchAll = true; break; } if (CatchDecl->getType()->isObjCIdType() || CatchDecl->getType()->isObjCQualifiedIdType()) { llvm::Value *IDEHType = CGM.getModule().getGlobalVariable("OBJC_EHTYPE_id"); if (!IDEHType) IDEHType = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy, false, llvm::GlobalValue::ExternalLinkage, 0, "OBJC_EHTYPE_id"); SelectorArgs.push_back(IDEHType); } else { // All other types should be Objective-C interface pointer types. const ObjCObjectPointerType *PT = CatchDecl->getType()->getAs(); assert(PT && "Invalid @catch type."); const ObjCInterfaceType *IT = PT->getInterfaceType(); assert(IT && "Invalid @catch type."); llvm::Value *EHType = GetInterfaceEHType(IT->getDecl(), false); SelectorArgs.push_back(EHType); } } } // We use a cleanup unless there was already a catch all. if (!HasCatchAll) { // Even though this is a cleanup, treat it as a catch all to avoid the C++ // personality behavior of terminating the process if only cleanups are // found in the exception handling stack. SelectorArgs.push_back(llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy)); Handlers.push_back(std::make_pair((const ParmVarDecl*) 0, (const Stmt*) 0)); } llvm::Value *Selector = CGF.Builder.CreateCall(llvm_eh_selector, SelectorArgs.begin(), SelectorArgs.end(), "selector"); for (unsigned i = 0, e = Handlers.size(); i != e; ++i) { const VarDecl *CatchParam = Handlers[i].first; const Stmt *CatchBody = Handlers[i].second; llvm::BasicBlock *Next = 0; // The last handler always matches. if (i + 1 != e) { assert(CatchParam && "Only last handler can be a catch all."); llvm::BasicBlock *Match = CGF.createBasicBlock("match"); Next = CGF.createBasicBlock("catch.next"); llvm::Value *Id = CGF.Builder.CreateCall(llvm_eh_typeid_for, CGF.Builder.CreateBitCast(SelectorArgs[i+2], ObjCTypes.Int8PtrTy)); CGF.Builder.CreateCondBr(CGF.Builder.CreateICmpEQ(Selector, Id), Match, Next); CGF.EmitBlock(Match); } if (CatchBody) { llvm::BasicBlock *MatchEnd = CGF.createBasicBlock("match.end"); // Cleanups must call objc_end_catch. CGF.PushCleanupBlock(MatchEnd); llvm::Value *ExcObject = CGF.Builder.CreateCall(ObjCTypes.getObjCBeginCatchFn(), Exc); // Bind the catch parameter if it exists. if (CatchParam) { ExcObject = CGF.Builder.CreateBitCast(ExcObject, CGF.ConvertType(CatchParam->getType())); // CatchParam is a ParmVarDecl because of the grammar // construction used to handle this, but for codegen purposes // we treat this as a local decl. CGF.EmitLocalBlockVarDecl(*CatchParam); CGF.Builder.CreateStore(ExcObject, CGF.GetAddrOfLocalVar(CatchParam)); } // Exceptions inside the catch block must be rethrown. We set a special // purpose invoke destination for this which just collects the thrown // exception and overwrites the object in RethrowPtr, branches through the // match.end to make sure we call objc_end_catch, before branching to the // rethrow handler. llvm::BasicBlock *MatchHandler = CGF.createBasicBlock("match.handler"); CGF.setInvokeDest(MatchHandler); CGF.ObjCEHValueStack.push_back(ExcObject); CGF.EmitStmt(CatchBody); CGF.ObjCEHValueStack.pop_back(); CGF.setInvokeDest(0); CGF.EmitBranchThroughCleanup(FinallyEnd); // Don't emit the extra match handler if there we no unprotected calls in // the catch block. if (MatchHandler->use_empty()) { delete MatchHandler; } else { CGF.EmitBlock(MatchHandler); llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); // We are required to emit this call to satisfy LLVM, even // though we don't use the result. CGF.Builder.CreateCall3(llvm_eh_selector, Exc, ObjCTypes.getEHPersonalityPtr(), llvm::ConstantInt::get( llvm::Type::getInt32Ty(VMContext), 0), "unused_eh_selector"); CGF.Builder.CreateStore(Exc, RethrowPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); } CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); CGF.EmitBlock(MatchEnd); // Unfortunately, we also have to generate another EH frame here // in case this throws. llvm::BasicBlock *MatchEndHandler = CGF.createBasicBlock("match.end.handler"); llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); CGF.Builder.CreateInvoke(ObjCTypes.getObjCEndCatchFn(), Cont, MatchEndHandler); CGF.EmitBlock(Cont); if (Info.SwitchBlock) CGF.EmitBlock(Info.SwitchBlock); if (Info.EndBlock) CGF.EmitBlock(Info.EndBlock); CGF.EmitBlock(MatchEndHandler); llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc"); // We are required to emit this call to satisfy LLVM, even // though we don't use the result. CGF.Builder.CreateCall3(llvm_eh_selector, Exc, ObjCTypes.getEHPersonalityPtr(), llvm::ConstantInt::get( llvm::Type::getInt32Ty(VMContext), 0), "unused_eh_selector"); CGF.Builder.CreateStore(Exc, RethrowPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); if (Next) CGF.EmitBlock(Next); } else { assert(!Next && "catchup should be last handler."); CGF.Builder.CreateStore(Exc, RethrowPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); } } // Pop the cleanup entry, the @finally is outside this cleanup // scope. CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock(); CGF.setInvokeDest(PrevLandingPad); CGF.EmitBlock(FinallyBlock); if (isTry) { if (const ObjCAtFinallyStmt* FinallyStmt = cast(S).getFinallyStmt()) CGF.EmitStmt(FinallyStmt->getFinallyBody()); } else { // Emit 'objc_sync_exit(expr)' as finally's sole statement for // @synchronized. CGF.Builder.CreateCall(ObjCTypes.getSyncExitFn(), SyncArg); } if (Info.SwitchBlock) CGF.EmitBlock(Info.SwitchBlock); if (Info.EndBlock) CGF.EmitBlock(Info.EndBlock); // Branch around the rethrow code. CGF.EmitBranch(FinallyEnd); // Generate the rethrow code, taking care to use an invoke if we are in a // nested exception scope. CGF.EmitBlock(FinallyRethrow); if (PrevLandingPad) { llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); CGF.Builder.CreateInvoke(ObjCTypes.getUnwindResumeOrRethrowFn(), Cont, PrevLandingPad, CGF.Builder.CreateLoad(RethrowPtr)); CGF.EmitBlock(Cont); } else { CGF.Builder.CreateCall(ObjCTypes.getUnwindResumeOrRethrowFn(), CGF.Builder.CreateLoad(RethrowPtr)); } CGF.Builder.CreateUnreachable(); CGF.EmitBlock(FinallyEnd); } /// EmitThrowStmt - Generate code for a throw statement. void CGObjCNonFragileABIMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S) { llvm::Value *Exception; if (const Expr *ThrowExpr = S.getThrowExpr()) { Exception = CGF.EmitScalarExpr(ThrowExpr); } else { assert((!CGF.ObjCEHValueStack.empty() && CGF.ObjCEHValueStack.back()) && "Unexpected rethrow outside @catch block."); Exception = CGF.ObjCEHValueStack.back(); } llvm::Value *ExceptionAsObject = CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp"); llvm::BasicBlock *InvokeDest = CGF.getInvokeDest(); if (InvokeDest) { llvm::BasicBlock *Cont = CGF.createBasicBlock("invoke.cont"); CGF.Builder.CreateInvoke(ObjCTypes.getExceptionThrowFn(), Cont, InvokeDest, &ExceptionAsObject, &ExceptionAsObject + 1); CGF.EmitBlock(Cont); } else CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), ExceptionAsObject); CGF.Builder.CreateUnreachable(); // Clear the insertion point to indicate we are in unreachable code. CGF.Builder.ClearInsertionPoint(); } llvm::Value * CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, bool ForDefinition) { llvm::GlobalVariable * &Entry = EHTypeReferences[ID->getIdentifier()]; // If we don't need a definition, return the entry if found or check // if we use an external reference. if (!ForDefinition) { if (Entry) return Entry; // If this type (or a super class) has the __objc_exception__ // attribute, emit an external reference. if (hasObjCExceptionAttribute(CGM.getContext(), ID)) return Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy, false, llvm::GlobalValue::ExternalLinkage, 0, ("OBJC_EHTYPE_$_" + ID->getIdentifier()->getName())); } // Otherwise we need to either make a new entry or fill in the // initializer. assert((!Entry || !Entry->hasInitializer()) && "Duplicate EHType definition"); std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); std::string VTableName = "objc_ehtype_vtable"; llvm::GlobalVariable *VTableGV = CGM.getModule().getGlobalVariable(VTableName); if (!VTableGV) VTableGV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.Int8PtrTy, false, llvm::GlobalValue::ExternalLinkage, 0, VTableName); llvm::Value *VTableIdx = llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), 2); std::vector Values(3); Values[0] = llvm::ConstantExpr::getGetElementPtr(VTableGV, &VTableIdx, 1); Values[1] = GetClassName(ID->getIdentifier()); Values[2] = GetClassGlobal(ClassName); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.EHTypeTy, Values); if (Entry) { Entry->setInitializer(Init); } else { Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy, false, llvm::GlobalValue::WeakAnyLinkage, Init, ("OBJC_EHTYPE_$_" + ID->getIdentifier()->getName())); } if (CGM.getLangOptions().getVisibilityMode() == LangOptions::Hidden) Entry->setVisibility(llvm::GlobalValue::HiddenVisibility); Entry->setAlignment(CGM.getTargetData().getABITypeAlignment( ObjCTypes.EHTypeTy)); if (ForDefinition) { Entry->setSection("__DATA,__objc_const"); Entry->setLinkage(llvm::GlobalValue::ExternalLinkage); } else { Entry->setSection("__DATA,__datacoal_nt,coalesced"); } return Entry; } /* *** */ CodeGen::CGObjCRuntime * CodeGen::CreateMacObjCRuntime(CodeGen::CodeGenModule &CGM) { return new CGObjCMac(CGM); } CodeGen::CGObjCRuntime * CodeGen::CreateMacNonFragileABIObjCRuntime(CodeGen::CodeGenModule &CGM) { return new CGObjCNonFragileABIMac(CGM); }