#include "CGBlocks.h"
#include "CGCleanup.h"
#include "CGObjCRuntime.h"
#include "CGRecordLayout.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtObjC.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/LangOptions.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/CodeGen/ConstantInitBuilder.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/UniqueVector.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>
using namespace clang;
using namespace CodeGen;
namespace {
class ObjCCommonTypesHelper {
protected:
llvm::LLVMContext &VMContext;
private:
llvm::FunctionCallee getMessageSendFn() const {
llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSend",
llvm::AttributeList::get(CGM.getLLVMContext(),
llvm::AttributeList::FunctionIndex,
llvm::Attribute::NonLazyBind));
}
llvm::FunctionCallee getMessageSendStretFn() const {
llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.VoidTy,
params, true),
"objc_msgSend_stret");
}
llvm::FunctionCallee getMessageSendFpretFn() const {
llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.DoubleTy,
params, true),
"objc_msgSend_fpret");
}
llvm::FunctionCallee getMessageSendFp2retFn() const {
llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy };
llvm::Type *longDoubleType = llvm::Type::getX86_FP80Ty(VMContext);
llvm::Type *resultType =
llvm::StructType::get(longDoubleType, longDoubleType);
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(resultType,
params, true),
"objc_msgSend_fp2ret");
}
llvm::FunctionCallee getMessageSendSuperFn() const {
llvm::Type *params[] = { SuperPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSendSuper");
}
llvm::FunctionCallee getMessageSendSuperFn2() const {
llvm::Type *params[] = { SuperPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSendSuper2");
}
llvm::FunctionCallee getMessageSendSuperStretFn() const {
llvm::Type *params[] = { Int8PtrTy, SuperPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(CGM.VoidTy, params, true),
"objc_msgSendSuper_stret");
}
llvm::FunctionCallee getMessageSendSuperStretFn2() const {
llvm::Type *params[] = { Int8PtrTy, SuperPtrTy, SelectorPtrTy };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(CGM.VoidTy, params, true),
"objc_msgSendSuper2_stret");
}
llvm::FunctionCallee getMessageSendSuperFpretFn() const {
return getMessageSendSuperFn();
}
llvm::FunctionCallee getMessageSendSuperFpretFn2() const {
return getMessageSendSuperFn2();
}
protected:
CodeGen::CodeGenModule &CGM;
public:
llvm::IntegerType *ShortTy, *IntTy, *LongTy;
llvm::PointerType *Int8PtrTy, *Int8PtrPtrTy;
llvm::PointerType *Int8PtrProgramASTy;
llvm::Type *IvarOffsetVarTy;
llvm::PointerType *ObjectPtrTy;
llvm::PointerType *PtrObjectPtrTy;
llvm::PointerType *SelectorPtrTy;
private:
llvm::Type *ExternalProtocolPtrTy;
public:
llvm::Type *getExternalProtocolPtrTy() {
if (!ExternalProtocolPtrTy) {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
llvm::Type *T = Types.ConvertType(Ctx.getObjCProtoType());
ExternalProtocolPtrTy = llvm::PointerType::getUnqual(T);
}
return ExternalProtocolPtrTy;
}
QualType SuperCTy;
QualType SuperPtrCTy;
llvm::StructType *SuperTy;
llvm::PointerType *SuperPtrTy;
llvm::StructType *PropertyTy;
llvm::StructType *PropertyListTy;
llvm::PointerType *PropertyListPtrTy;
llvm::StructType *MethodTy;
llvm::Type *CacheTy;
llvm::PointerType *CachePtrTy;
llvm::FunctionCallee getGetPropertyFn() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType());
CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType());
CanQualType Params[] = {
IdType, SelType,
Ctx.getPointerDiffType()->getCanonicalTypeUnqualified(), Ctx.BoolTy};
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(IdType, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_getProperty");
}
llvm::FunctionCallee getSetPropertyFn() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType());
CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType());
CanQualType Params[] = {
IdType,
SelType,
Ctx.getPointerDiffType()->getCanonicalTypeUnqualified(),
IdType,
Ctx.BoolTy,
Ctx.BoolTy};
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_setProperty");
}
llvm::FunctionCallee getOptimizedSetPropertyFn(bool atomic, bool copy) {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
SmallVector<CanQualType,4> Params;
CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType());
CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType());
Params.push_back(IdType);
Params.push_back(SelType);
Params.push_back(IdType);
Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified());
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
const char *name;
if (atomic && copy)
name = "objc_setProperty_atomic_copy";
else if (atomic && !copy)
name = "objc_setProperty_atomic";
else if (!atomic && copy)
name = "objc_setProperty_nonatomic_copy";
else
name = "objc_setProperty_nonatomic";
return CGM.CreateRuntimeFunction(FTy, name);
}
llvm::FunctionCallee getCopyStructFn() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
SmallVector<CanQualType,5> Params;
Params.push_back(Ctx.VoidPtrTy);
Params.push_back(Ctx.VoidPtrTy);
Params.push_back(Ctx.getSizeType());
Params.push_back(Ctx.BoolTy);
Params.push_back(Ctx.BoolTy);
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_copyStruct");
}
llvm::FunctionCallee getCppAtomicObjectFunction() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
SmallVector<CanQualType,3> Params;
Params.push_back(Ctx.VoidPtrTy);
Params.push_back(Ctx.VoidPtrTy);
Params.push_back(Ctx.VoidPtrTy);
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_copyCppObjectAtomic");
}
llvm::FunctionCallee getEnumerationMutationFn() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
SmallVector<CanQualType,1> Params;
Params.push_back(Ctx.getCanonicalParamType(Ctx.getObjCIdType()));
llvm::FunctionType *FTy =
Types.GetFunctionType(
Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation");
}
llvm::FunctionCallee getLookUpClassFn() {
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
SmallVector<CanQualType,1> Params;
Params.push_back(
Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst())));
llvm::FunctionType *FTy =
Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration(
Ctx.getCanonicalType(Ctx.getObjCClassType()),
Params));
return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass");
}
llvm::FunctionCallee getGcReadWeakFn() {
llvm::Type *args[] = { ObjectPtrTy->getPointerTo() };
llvm::FunctionType *FTy =
llvm::FunctionType::get(ObjectPtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_read_weak");
}
llvm::FunctionCallee getGcAssignWeakFn() {
llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() };
llvm::FunctionType *FTy =
llvm::FunctionType::get(ObjectPtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_assign_weak");
}
llvm::FunctionCallee getGcAssignGlobalFn() {
llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() };
llvm::FunctionType *FTy =
llvm::FunctionType::get(ObjectPtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_assign_global");
}
llvm::FunctionCallee getGcAssignThreadLocalFn() {
llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() };
llvm::FunctionType *FTy =
llvm::FunctionType::get(ObjectPtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_assign_threadlocal");
}
llvm::FunctionCallee getGcAssignIvarFn() {
llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo(),
CGM.PtrDiffTy };
llvm::FunctionType *FTy =
llvm::FunctionType::get(ObjectPtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_assign_ivar");
}
llvm::FunctionCallee GcMemmoveCollectableFn() {
llvm::Type *args[] = { Int8PtrTy, Int8PtrTy, LongTy };
llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_memmove_collectable");
}
llvm::FunctionCallee getGcAssignStrongCastFn() {
llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() };
llvm::FunctionType *FTy =
llvm::FunctionType::get(ObjectPtrTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_assign_strongCast");
}
llvm::FunctionCallee getExceptionThrowFn() {
llvm::Type *args[] = { ObjectPtrTy };
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGM.VoidTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_exception_throw");
}
llvm::FunctionCallee getExceptionRethrowFn() {
llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, false);
return CGM.CreateRuntimeFunction(FTy, "objc_exception_rethrow");
}
llvm::FunctionCallee getSyncEnterFn() {
llvm::Type *args[] = { ObjectPtrTy };
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGM.IntTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_sync_enter");
}
llvm::FunctionCallee getSyncExitFn() {
llvm::Type *args[] = { ObjectPtrTy };
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGM.IntTy, args, false);
return CGM.CreateRuntimeFunction(FTy, "objc_sync_exit");
}
llvm::FunctionCallee getSendFn(bool IsSuper) const {
return IsSuper ? getMessageSendSuperFn() : getMessageSendFn();
}
llvm::FunctionCallee getSendFn2(bool IsSuper) const {
return IsSuper ? getMessageSendSuperFn2() : getMessageSendFn();
}
llvm::FunctionCallee getSendStretFn(bool IsSuper) const {
return IsSuper ? getMessageSendSuperStretFn() : getMessageSendStretFn();
}
llvm::FunctionCallee getSendStretFn2(bool IsSuper) const {
return IsSuper ? getMessageSendSuperStretFn2() : getMessageSendStretFn();
}
llvm::FunctionCallee getSendFpretFn(bool IsSuper) const {
return IsSuper ? getMessageSendSuperFpretFn() : getMessageSendFpretFn();
}
llvm::FunctionCallee getSendFpretFn2(bool IsSuper) const {
return IsSuper ? getMessageSendSuperFpretFn2() : getMessageSendFpretFn();
}
llvm::FunctionCallee getSendFp2retFn(bool IsSuper) const {
return IsSuper ? getMessageSendSuperFn() : getMessageSendFp2retFn();
}
llvm::FunctionCallee getSendFp2RetFn2(bool IsSuper) const {
return IsSuper ? getMessageSendSuperFn2() : getMessageSendFp2retFn();
}
ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm);
};
class ObjCTypesHelper : public ObjCCommonTypesHelper {
public:
llvm::StructType *SymtabTy;
llvm::PointerType *SymtabPtrTy;
llvm::StructType *ModuleTy;
llvm::StructType *ProtocolTy;
llvm::PointerType *ProtocolPtrTy;
llvm::StructType *ProtocolExtensionTy;
llvm::PointerType *ProtocolExtensionPtrTy;
llvm::StructType *MethodDescriptionTy;
llvm::StructType *MethodDescriptionListTy;
llvm::PointerType *MethodDescriptionListPtrTy;
llvm::StructType *ProtocolListTy;
llvm::PointerType *ProtocolListPtrTy;
llvm::StructType *CategoryTy;
llvm::StructType *ClassTy;
llvm::PointerType *ClassPtrTy;
llvm::StructType *ClassExtensionTy;
llvm::PointerType *ClassExtensionPtrTy;
llvm::StructType *IvarTy;
llvm::StructType *IvarListTy;
llvm::PointerType *IvarListPtrTy;
llvm::StructType *MethodListTy;
llvm::PointerType *MethodListPtrTy;
llvm::StructType *ExceptionDataTy;
llvm::FunctionCallee getExceptionTryEnterFn() {
llvm::Type *params[] = { ExceptionDataTy->getPointerTo() };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(CGM.VoidTy, params, false),
"objc_exception_try_enter");
}
llvm::FunctionCallee getExceptionTryExitFn() {
llvm::Type *params[] = { ExceptionDataTy->getPointerTo() };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(CGM.VoidTy, params, false),
"objc_exception_try_exit");
}
llvm::FunctionCallee getExceptionExtractFn() {
llvm::Type *params[] = { ExceptionDataTy->getPointerTo() };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, false),
"objc_exception_extract");
}
llvm::FunctionCallee getExceptionMatchFn() {
llvm::Type *params[] = { ClassPtrTy, ObjectPtrTy };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(CGM.Int32Ty, params, false),
"objc_exception_match");
}
llvm::FunctionCallee getSetJmpFn() {
llvm::Type *params[] = { CGM.Int32Ty->getPointerTo() };
return CGM.CreateRuntimeFunction(
llvm::FunctionType::get(CGM.Int32Ty, params, false), "_setjmp",
llvm::AttributeList::get(CGM.getLLVMContext(),
llvm::AttributeList::FunctionIndex,
llvm::Attribute::NonLazyBind));
}
public:
ObjCTypesHelper(CodeGen::CodeGenModule &cgm);
};
class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper {
public:
llvm::StructType *MethodListnfABITy;
llvm::PointerType *MethodListnfABIPtrTy;
llvm::StructType *ProtocolnfABITy;
llvm::PointerType *ProtocolnfABIPtrTy;
llvm::StructType *ProtocolListnfABITy;
llvm::PointerType *ProtocolListnfABIPtrTy;
llvm::StructType *ClassnfABITy;
llvm::PointerType *ClassnfABIPtrTy;
llvm::StructType *IvarnfABITy;
llvm::StructType *IvarListnfABITy;
llvm::PointerType *IvarListnfABIPtrTy;
llvm::StructType *ClassRonfABITy;
llvm::PointerType *ImpnfABITy;
llvm::StructType *CategorynfABITy;
llvm::StructType *MessageRefTy;
QualType MessageRefCTy;
llvm::Type *MessageRefPtrTy;
QualType MessageRefCPtrTy;
llvm::StructType *SuperMessageRefTy;
llvm::PointerType *SuperMessageRefPtrTy;
llvm::FunctionCallee getMessageSendFixupFn() {
llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSend_fixup");
}
llvm::FunctionCallee getMessageSendFpretFixupFn() {
llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSend_fpret_fixup");
}
llvm::FunctionCallee getMessageSendStretFixupFn() {
llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSend_stret_fixup");
}
llvm::FunctionCallee getMessageSendSuper2FixupFn() {
llvm::Type *params[] = { SuperPtrTy, SuperMessageRefPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSendSuper2_fixup");
}
llvm::FunctionCallee getMessageSendSuper2StretFixupFn() {
llvm::Type *params[] = { SuperPtrTy, SuperMessageRefPtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy,
params, true),
"objc_msgSendSuper2_stret_fixup");
}
llvm::FunctionCallee getObjCEndCatchFn() {
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.VoidTy, false),
"objc_end_catch");
}
llvm::FunctionCallee getObjCBeginCatchFn() {
llvm::Type *params[] = { Int8PtrTy };
return CGM.CreateRuntimeFunction(llvm::FunctionType::get(Int8PtrTy,
params, false),
"objc_begin_catch");
}
llvm::FunctionCallee getLoadClassrefFn() const {
llvm::Type *params[] = { Int8PtrPtrTy };
llvm::LLVMContext &C = CGM.getLLVMContext();
llvm::AttributeSet AS = llvm::AttributeSet::get(C, {
llvm::Attribute::get(C, llvm::Attribute::NonLazyBind),
llvm::Attribute::getWithMemoryEffects(C, llvm::MemoryEffects::none()),
llvm::Attribute::get(C, llvm::Attribute::NoUnwind),
});
llvm::FunctionCallee F = CGM.CreateRuntimeFunction(
llvm::FunctionType::get(ClassnfABIPtrTy, params, false),
"objc_loadClassref",
llvm::AttributeList::get(CGM.getLLVMContext(),
llvm::AttributeList::FunctionIndex, AS));
if (!CGM.getTriple().isOSBinFormatCOFF())
cast<llvm::Function>(F.getCallee())->setLinkage(
llvm::Function::ExternalWeakLinkage);
return F;
}
llvm::StructType *EHTypeTy;
llvm::Type *EHTypePtrTy;
ObjCNonFragileABITypesHelper(CodeGen::CodeGenModule &cgm);
};
enum class ObjCLabelType {
ClassName,
MethodVarName,
MethodVarType,
PropertyName,
};
class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
public:
class SKIP_SCAN {
public:
unsigned skip;
unsigned scan;
SKIP_SCAN(unsigned _skip = 0, unsigned _scan = 0)
: skip(_skip), scan(_scan) {}
};
enum BLOCK_LAYOUT_OPCODE {
BLOCK_LAYOUT_OPERATOR = 0,
BLOCK_LAYOUT_NON_OBJECT_BYTES = 1,
BLOCK_LAYOUT_NON_OBJECT_WORDS = 2,
BLOCK_LAYOUT_STRONG = 3,
BLOCK_LAYOUT_BYREF = 4,
BLOCK_LAYOUT_WEAK = 5,
BLOCK_LAYOUT_UNRETAINED = 6
};
class RUN_SKIP {
public:
enum BLOCK_LAYOUT_OPCODE opcode;
CharUnits block_var_bytepos;
CharUnits block_var_size;
RUN_SKIP(enum BLOCK_LAYOUT_OPCODE Opcode = BLOCK_LAYOUT_OPERATOR,
CharUnits BytePos = CharUnits::Zero(),
CharUnits Size = CharUnits::Zero())
: opcode(Opcode), block_var_bytepos(BytePos), block_var_size(Size) {}
bool operator<(const RUN_SKIP &b) const {
return block_var_bytepos < b.block_var_bytepos;
}
};
protected:
llvm::LLVMContext &VMContext;
unsigned ObjCABI;
SmallVector<RUN_SKIP, 16> RunSkipBlockVars;
llvm::SetVector<IdentifierInfo*> LazySymbols;
llvm::SetVector<IdentifierInfo*> DefinedSymbols;
llvm::StringMap<llvm::GlobalVariable*> ClassNames;
llvm::DenseMap<Selector, llvm::GlobalVariable*> MethodVarNames;
llvm::SmallSetVector<llvm::CachedHashString, 16> DefinedCategoryNames;
llvm::StringMap<llvm::GlobalVariable*> MethodVarTypes;
llvm::DenseMap<const ObjCMethodDecl*, llvm::Function*> MethodDefinitions;
llvm::DenseMap<const ObjCMethodDecl*, llvm::Function*> DirectMethodDefinitions;
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> PropertyNames;
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> ClassReferences;
llvm::DenseMap<Selector, llvm::GlobalVariable*> SelectorReferences;
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> Protocols;
llvm::DenseSet<IdentifierInfo*> DefinedProtocols;
SmallVector<llvm::GlobalValue*, 16> DefinedClasses;
SmallVector<const ObjCInterfaceDecl*, 16> ImplementedClasses;
SmallVector<llvm::GlobalValue*, 16> DefinedNonLazyClasses;
SmallVector<llvm::GlobalValue*, 16> DefinedCategories;
SmallVector<llvm::GlobalValue*, 16> DefinedStubCategories;
SmallVector<llvm::GlobalValue*, 16> DefinedNonLazyCategories;
llvm::WeakTrackingVH ConstantStringClassRef;
llvm::StructType *NSConstantStringType = nullptr;
llvm::StringMap<llvm::GlobalVariable *> NSConstantStringMap;
llvm::Constant *GetMethodVarName(Selector Sel);
llvm::Constant *GetMethodVarName(IdentifierInfo *Ident);
llvm::Constant *GetMethodVarType(const ObjCMethodDecl *D,
bool Extended = false);
llvm::Constant *GetMethodVarType(const FieldDecl *D);
llvm::Constant *GetPropertyName(IdentifierInfo *Ident);
llvm::Constant *GetPropertyTypeString(const ObjCPropertyDecl *PD,
const Decl *Container);
llvm::Constant *GetClassName(StringRef RuntimeName);
llvm::Function *GetMethodDefinition(const ObjCMethodDecl *MD);
llvm::Constant *BuildIvarLayout(const ObjCImplementationDecl *OI,
CharUnits beginOffset,
CharUnits endOffset,
bool forStrongLayout,
bool hasMRCWeakIvars);
llvm::Constant *BuildStrongIvarLayout(const ObjCImplementationDecl *OI,
CharUnits beginOffset,
CharUnits endOffset) {
return BuildIvarLayout(OI, beginOffset, endOffset, true, false);
}
llvm::Constant *BuildWeakIvarLayout(const ObjCImplementationDecl *OI,
CharUnits beginOffset,
CharUnits endOffset,
bool hasMRCWeakIvars) {
return BuildIvarLayout(OI, beginOffset, endOffset, false, hasMRCWeakIvars);
}
Qualifiers::ObjCLifetime getBlockCaptureLifetime(QualType QT, bool ByrefLayout);
void UpdateRunSkipBlockVars(bool IsByref,
Qualifiers::ObjCLifetime LifeTime,
CharUnits FieldOffset,
CharUnits FieldSize);
void BuildRCBlockVarRecordLayout(const RecordType *RT,
CharUnits BytePos, bool &HasUnion,
bool ByrefLayout=false);
void BuildRCRecordLayout(const llvm::StructLayout *RecLayout,
const RecordDecl *RD,
ArrayRef<const FieldDecl*> RecFields,
CharUnits BytePos, bool &HasUnion,
bool ByrefLayout);
uint64_t InlineLayoutInstruction(SmallVectorImpl<unsigned char> &Layout);
llvm::Constant *getBitmapBlockLayout(bool ComputeByrefLayout);
llvm::Constant *GetIvarLayoutName(IdentifierInfo *Ident,
const ObjCCommonTypesHelper &ObjCTypes);
llvm::Constant *EmitPropertyList(Twine Name,
const Decl *Container,
const ObjCContainerDecl *OCD,
const ObjCCommonTypesHelper &ObjCTypes,
bool IsClassProperty);
llvm::Constant *EmitProtocolMethodTypes(Twine Name,
ArrayRef<llvm::Constant*> MethodTypes,
const ObjCCommonTypesHelper &ObjCTypes);
llvm::Constant *GetProtocolRef(const ObjCProtocolDecl *PD);
llvm::Value *EmitClassRefViaRuntime(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID,
ObjCCommonTypesHelper &ObjCTypes);
std::string GetSectionName(StringRef Section, StringRef MachOAttributes);
public:
llvm::GlobalVariable *CreateMetadataVar(Twine Name,
ConstantStructBuilder &Init,
StringRef Section, CharUnits Align,
bool AddToUsed);
llvm::GlobalVariable *CreateMetadataVar(Twine Name,
llvm::Constant *Init,
StringRef Section, CharUnits Align,
bool AddToUsed);
llvm::GlobalVariable *CreateCStringLiteral(StringRef Name,
ObjCLabelType LabelType,
bool ForceNonFragileABI = false,
bool NullTerminate = true);
protected:
CodeGen::RValue EmitMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType,
Selector Sel,
llvm::Value *Arg0,
QualType Arg0Ty,
bool IsSuper,
const CallArgList &CallArgs,
const ObjCMethodDecl *OMD,
const ObjCInterfaceDecl *ClassReceiver,
const ObjCCommonTypesHelper &ObjCTypes);
void EmitImageInfo();
public:
CGObjCCommonMac(CodeGen::CodeGenModule &cgm)
: CGObjCRuntime(cgm), VMContext(cgm.getLLVMContext()) {}
bool isNonFragileABI() const {
return ObjCABI == 2;
}
ConstantAddress GenerateConstantString(const StringLiteral *SL) override;
ConstantAddress GenerateConstantNSString(const StringLiteral *SL);
llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD=nullptr) override;
llvm::Function *GenerateDirectMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD);
void GenerateDirectMethodPrologue(CodeGenFunction &CGF, llvm::Function *Fn,
const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) override;
void GenerateProtocol(const ObjCProtocolDecl *PD) override;
virtual llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD)=0;
virtual llvm::Constant *getNSConstantStringClassRef() = 0;
llvm::Constant *BuildGCBlockLayout(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) override;
llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) override;
std::string getRCBlockLayoutStr(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) override;
llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM,
QualType T) override;
private:
void fillRunSkipBlockVars(CodeGenModule &CGM, const CGBlockInfo &blockInfo);
};
namespace {
enum class MethodListType {
CategoryInstanceMethods,
CategoryClassMethods,
InstanceMethods,
ClassMethods,
ProtocolInstanceMethods,
ProtocolClassMethods,
OptionalProtocolInstanceMethods,
OptionalProtocolClassMethods,
};
class ProtocolMethodLists {
public:
enum Kind {
RequiredInstanceMethods,
RequiredClassMethods,
OptionalInstanceMethods,
OptionalClassMethods
};
enum {
NumProtocolMethodLists = 4
};
static MethodListType getMethodListKind(Kind kind) {
switch (kind) {
case RequiredInstanceMethods:
return MethodListType::ProtocolInstanceMethods;
case RequiredClassMethods:
return MethodListType::ProtocolClassMethods;
case OptionalInstanceMethods:
return MethodListType::OptionalProtocolInstanceMethods;
case OptionalClassMethods:
return MethodListType::OptionalProtocolClassMethods;
}
llvm_unreachable("bad kind");
}
SmallVector<const ObjCMethodDecl *, 4> Methods[NumProtocolMethodLists];
static ProtocolMethodLists get(const ObjCProtocolDecl *PD) {
ProtocolMethodLists result;
for (auto *MD : PD->methods()) {
size_t index = (2 * size_t(MD->isOptional()))
+ (size_t(MD->isClassMethod()));
result.Methods[index].push_back(MD);
}
return result;
}
template <class Self>
SmallVector<llvm::Constant*, 8> emitExtendedTypesArray(Self *self) const {
SmallVector<llvm::Constant*, 8> result;
for (auto &list : Methods) {
for (auto MD : list) {
result.push_back(self->GetMethodVarType(MD, true));
}
}
return result;
}
template <class Self>
llvm::Constant *emitMethodList(Self *self, const ObjCProtocolDecl *PD,
Kind kind) const {
return self->emitMethodList(PD->getObjCRuntimeNameAsString(),
getMethodListKind(kind), Methods[kind]);
}
};
}
class CGObjCMac : public CGObjCCommonMac {
private:
friend ProtocolMethodLists;
ObjCTypesHelper ObjCTypes;
void EmitModuleInfo();
llvm::Constant *EmitModuleSymbols();
void FinishModule();
llvm::Constant *EmitClassExtension(const ObjCImplementationDecl *ID,
CharUnits instanceSize,
bool hasMRCWeakIvars,
bool isMetaclass);
llvm::Value *EmitClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID);
llvm::Value *EmitClassRefFromId(CodeGenFunction &CGF,
IdentifierInfo *II);
llvm::Value *EmitNSAutoreleasePoolClassRef(CodeGenFunction &CGF) override;
llvm::Value *EmitSuperClassRef(const ObjCInterfaceDecl *ID);
llvm::Constant *EmitIvarList(const ObjCImplementationDecl *ID,
bool ForClass);
llvm::Constant *EmitMetaClassRef(const ObjCInterfaceDecl *ID);
llvm::Constant *EmitMetaClass(const ObjCImplementationDecl *ID,
llvm::Constant *Protocols,
ArrayRef<const ObjCMethodDecl *> Methods);
void emitMethodConstant(ConstantArrayBuilder &builder,
const ObjCMethodDecl *MD);
void emitMethodDescriptionConstant(ConstantArrayBuilder &builder,
const ObjCMethodDecl *MD);
llvm::Constant *emitMethodList(Twine Name, MethodListType MLT,
ArrayRef<const ObjCMethodDecl *> Methods);
llvm::Constant *GetOrEmitProtocol(const ObjCProtocolDecl *PD) override;
llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD) override;
llvm::Constant *
EmitProtocolExtension(const ObjCProtocolDecl *PD,
const ProtocolMethodLists &methodLists);
llvm::Constant *EmitProtocolList(Twine Name,
ObjCProtocolDecl::protocol_iterator begin,
ObjCProtocolDecl::protocol_iterator end);
llvm::Value *EmitSelector(CodeGenFunction &CGF, Selector Sel);
ConstantAddress EmitSelectorAddr(Selector Sel);
public:
CGObjCMac(CodeGen::CodeGenModule &cgm);
llvm::Constant *getNSConstantStringClassRef() override;
llvm::Function *ModuleInitFunction() override;
CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType,
Selector Sel, llvm::Value *Receiver,
const CallArgList &CallArgs,
const ObjCInterfaceDecl *Class,
const ObjCMethodDecl *Method) override;
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) override;
llvm::Value *GetClass(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) override;
llvm::Value *GetSelector(CodeGenFunction &CGF, Selector Sel) override;
Address GetAddrOfSelector(CodeGenFunction &CGF, Selector Sel) override;
llvm::Value *GetSelector(CodeGenFunction &CGF,
const ObjCMethodDecl *Method) override;
llvm::Constant *GetEHType(QualType T) override;
void GenerateCategory(const ObjCCategoryImplDecl *CMD) override;
void GenerateClass(const ObjCImplementationDecl *ClassDecl) override;
void RegisterAlias(const ObjCCompatibleAliasDecl *OAD) override {}
llvm::Value *GenerateProtocolRef(CodeGenFunction &CGF,
const ObjCProtocolDecl *PD) override;
llvm::FunctionCallee GetPropertyGetFunction() override;
llvm::FunctionCallee GetPropertySetFunction() override;
llvm::FunctionCallee GetOptimizedPropertySetFunction(bool atomic,
bool copy) override;
llvm::FunctionCallee GetGetStructFunction() override;
llvm::FunctionCallee GetSetStructFunction() override;
llvm::FunctionCallee GetCppAtomicObjectGetFunction() override;
llvm::FunctionCallee GetCppAtomicObjectSetFunction() override;
llvm::FunctionCallee EnumerationMutationFunction() override;
void EmitTryStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtTryStmt &S) override;
void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtSynchronizedStmt &S) override;
void EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, const Stmt &S);
void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S,
bool ClearInsertionPoint=true) override;
llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF,
Address AddrWeakObj) override;
void EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst) override;
void EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dest,
bool threadlocal = false) override;
void EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dest,
llvm::Value *ivarOffset) override;
void EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dest) override;
void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
Address dest, Address src,
llvm::Value *size) override;
LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy,
llvm::Value *BaseValue, const ObjCIvarDecl *Ivar,
unsigned CVRQualifiers) override;
llvm::Value *EmitIvarOffset(CodeGen::CodeGenFunction &CGF,
const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar) override;
};
class CGObjCNonFragileABIMac : public CGObjCCommonMac {
private:
friend ProtocolMethodLists;
ObjCNonFragileABITypesHelper ObjCTypes;
llvm::GlobalVariable* ObjCEmptyCacheVar;
llvm::Constant* ObjCEmptyVtableVar;
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> SuperClassReferences;
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> MetaClassReferences;
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> EHTypeReferences;
llvm::DenseSet<Selector> VTableDispatchMethods;
std::vector<llvm::GlobalValue*> DefinedMetaClasses;
bool isVTableDispatchedSelector(Selector Sel);
void FinishNonFragileABIModule();
void AddModuleClassList(ArrayRef<llvm::GlobalValue *> Container,
StringRef SymbolName, StringRef SectionName);
llvm::GlobalVariable * BuildClassRoTInitializer(unsigned flags,
unsigned InstanceStart,
unsigned InstanceSize,
const ObjCImplementationDecl *ID);
llvm::GlobalVariable *BuildClassObject(const ObjCInterfaceDecl *CI,
bool isMetaclass,
llvm::Constant *IsAGV,
llvm::Constant *SuperClassGV,
llvm::Constant *ClassRoGV,
bool HiddenVisibility);
void emitMethodConstant(ConstantArrayBuilder &builder,
const ObjCMethodDecl *MD,
bool forProtocol);
llvm::Constant *emitMethodList(Twine Name, MethodListType MLT,
ArrayRef<const ObjCMethodDecl *> Methods);
llvm::Constant *EmitIvarList(const ObjCImplementationDecl *ID);
llvm::Constant *EmitIvarOffsetVar(const ObjCInterfaceDecl *ID,
const ObjCIvarDecl *Ivar,
unsigned long int offset);
llvm::Constant *GetOrEmitProtocol(const ObjCProtocolDecl *PD) override;
llvm::Constant *GetOrEmitProtocolRef(const ObjCProtocolDecl *PD) override;
llvm::Constant *EmitProtocolList(Twine Name,
ObjCProtocolDecl::protocol_iterator begin,
ObjCProtocolDecl::protocol_iterator end);
CodeGen::RValue EmitVTableMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType,
Selector Sel,
llvm::Value *Receiver,
QualType Arg0Ty,
bool IsSuper,
const CallArgList &CallArgs,
const ObjCMethodDecl *Method);
llvm::Constant *GetClassGlobal(StringRef Name,
ForDefinition_t IsForDefinition,
bool Weak = false, bool DLLImport = false);
llvm::Constant *GetClassGlobal(const ObjCInterfaceDecl *ID,
bool isMetaclass,
ForDefinition_t isForDefinition);
llvm::Constant *GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID);
llvm::Value *EmitLoadOfClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID,
llvm::GlobalVariable *Entry);
llvm::Value *EmitClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID);
llvm::Value *EmitClassRefFromId(CodeGenFunction &CGF,
IdentifierInfo *II,
const ObjCInterfaceDecl *ID);
llvm::Value *EmitNSAutoreleasePoolClassRef(CodeGenFunction &CGF) override;
llvm::Value *EmitSuperClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID);
llvm::Value *EmitMetaClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID, bool Weak);
llvm::GlobalVariable * ObjCIvarOffsetVariable(
const ObjCInterfaceDecl *ID,
const ObjCIvarDecl *Ivar);
llvm::Value *EmitSelector(CodeGenFunction &CGF, Selector Sel);
ConstantAddress EmitSelectorAddr(Selector Sel);
llvm::Constant *GetInterfaceEHType(const ObjCInterfaceDecl *ID,
ForDefinition_t IsForDefinition);
StringRef getMetaclassSymbolPrefix() const { return "OBJC_METACLASS_$_"; }
StringRef getClassSymbolPrefix() const { return "OBJC_CLASS_$_"; }
void GetClassSizeInfo(const ObjCImplementationDecl *OID,
uint32_t &InstanceStart,
uint32_t &InstanceSize);
Selector GetNullarySelector(const char* name) const {
const IdentifierInfo *II = &CGM.getContext().Idents.get(name);
return CGM.getContext().Selectors.getSelector(0, &II);
}
Selector GetUnarySelector(const char* name) const {
const IdentifierInfo *II = &CGM.getContext().Idents.get(name);
return CGM.getContext().Selectors.getSelector(1, &II);
}
bool ImplementationIsNonLazy(const ObjCImplDecl *OD) const;
bool IsIvarOffsetKnownIdempotent(const CodeGen::CodeGenFunction &CGF,
const ObjCIvarDecl *IV) {
if (const ObjCMethodDecl *MD =
dyn_cast_or_null<ObjCMethodDecl>(CGF.CurFuncDecl))
if (MD->isInstanceMethod() && !MD->isDirectMethod())
if (const ObjCInterfaceDecl *ID = MD->getClassInterface())
return IV->getContainingInterface()->isSuperClassOf(ID);
return false;
}
bool isClassLayoutKnownStatically(const ObjCInterfaceDecl *ID) {
for (; ID; ID = ID->getSuperClass()) {
if (ID->getIdentifier()->getName() == "NSObject")
return true;
if (!ID->getImplementation())
return false;
}
return false;
}
public:
CGObjCNonFragileABIMac(CodeGen::CodeGenModule &cgm);
llvm::Constant *getNSConstantStringClassRef() override;
llvm::Function *ModuleInitFunction() override;
CodeGen::RValue GenerateMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType, Selector Sel,
llvm::Value *Receiver,
const CallArgList &CallArgs,
const ObjCInterfaceDecl *Class,
const ObjCMethodDecl *Method) override;
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) override;
llvm::Value *GetClass(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) override;
llvm::Value *GetSelector(CodeGenFunction &CGF, Selector Sel) override
{ return EmitSelector(CGF, Sel); }
Address GetAddrOfSelector(CodeGenFunction &CGF, Selector Sel) override
{ return EmitSelectorAddr(Sel); }
llvm::Value *GetSelector(CodeGenFunction &CGF,
const ObjCMethodDecl *Method) override
{ return EmitSelector(CGF, Method->getSelector()); }
void GenerateCategory(const ObjCCategoryImplDecl *CMD) override;
void GenerateClass(const ObjCImplementationDecl *ClassDecl) override;
void RegisterAlias(const ObjCCompatibleAliasDecl *OAD) override {}
llvm::Value *GenerateProtocolRef(CodeGenFunction &CGF,
const ObjCProtocolDecl *PD) override;
llvm::Constant *GetEHType(QualType T) override;
llvm::FunctionCallee GetPropertyGetFunction() override {
return ObjCTypes.getGetPropertyFn();
}
llvm::FunctionCallee GetPropertySetFunction() override {
return ObjCTypes.getSetPropertyFn();
}
llvm::FunctionCallee GetOptimizedPropertySetFunction(bool atomic,
bool copy) override {
return ObjCTypes.getOptimizedSetPropertyFn(atomic, copy);
}
llvm::FunctionCallee GetSetStructFunction() override {
return ObjCTypes.getCopyStructFn();
}
llvm::FunctionCallee GetGetStructFunction() override {
return ObjCTypes.getCopyStructFn();
}
llvm::FunctionCallee GetCppAtomicObjectSetFunction() override {
return ObjCTypes.getCppAtomicObjectFunction();
}
llvm::FunctionCallee GetCppAtomicObjectGetFunction() override {
return ObjCTypes.getCppAtomicObjectFunction();
}
llvm::FunctionCallee EnumerationMutationFunction() override {
return ObjCTypes.getEnumerationMutationFn();
}
void EmitTryStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtTryStmt &S) override;
void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtSynchronizedStmt &S) override;
void EmitThrowStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtThrowStmt &S,
bool ClearInsertionPoint=true) override;
llvm::Value * EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF,
Address AddrWeakObj) override;
void EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address edst) override;
void EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dest,
bool threadlocal = false) override;
void EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dest,
llvm::Value *ivarOffset) override;
void EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dest) override;
void EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
Address dest, Address src,
llvm::Value *size) override;
LValue EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF, QualType ObjectTy,
llvm::Value *BaseValue, const ObjCIvarDecl *Ivar,
unsigned CVRQualifiers) override;
llvm::Value *EmitIvarOffset(CodeGen::CodeGenFunction &CGF,
const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar) override;
};
struct NullReturnState {
llvm::BasicBlock *NullBB = nullptr;
NullReturnState() = default;
void init(CodeGenFunction &CGF, llvm::Value *receiver) {
NullBB = CGF.createBasicBlock("msgSend.null-receiver");
llvm::BasicBlock *callBB = CGF.createBasicBlock("msgSend.call");
llvm::Value *isNull = CGF.Builder.CreateIsNull(receiver);
CGF.Builder.CreateCondBr(isNull, NullBB, callBB);
CGF.EmitBlock(callBB);
}
RValue complete(CodeGenFunction &CGF,
ReturnValueSlot returnSlot,
RValue result,
QualType resultType,
const CallArgList &CallArgs,
const ObjCMethodDecl *Method) {
if (!NullBB) return result;
llvm::BasicBlock *contBB = nullptr;
llvm::BasicBlock *callBB = CGF.Builder.GetInsertBlock();
if (callBB) {
contBB = CGF.createBasicBlock("msgSend.cont");
CGF.Builder.CreateBr(contBB);
}
CGF.EmitBlock(NullBB);
if (Method) {
CGObjCRuntime::destroyCalleeDestroyedArguments(CGF, Method, CallArgs);
}
assert(CGF.Builder.GetInsertBlock() == NullBB);
if (result.isScalar() && resultType->isVoidType()) {
if (contBB) CGF.EmitBlock(contBB);
return result;
}
if (result.isScalar()) {
llvm::Value *null =
CGF.EmitFromMemory(CGF.CGM.EmitNullConstant(resultType), resultType);
if (!contBB) return RValue::get(null);
CGF.EmitBlock(contBB);
llvm::PHINode *phi = CGF.Builder.CreatePHI(null->getType(), 2);
phi->addIncoming(result.getScalarVal(), callBB);
phi->addIncoming(null, NullBB);
return RValue::get(phi);
}
if (result.isAggregate()) {
assert(result.isAggregate() && "null init of non-aggregate result?");
if (!returnSlot.isUnused())
CGF.EmitNullInitialization(result.getAggregateAddress(), resultType);
if (contBB) CGF.EmitBlock(contBB);
return result;
}
CGF.EmitBlock(contBB);
CodeGenFunction::ComplexPairTy callResult = result.getComplexVal();
llvm::Type *scalarTy = callResult.first->getType();
llvm::Constant *scalarZero = llvm::Constant::getNullValue(scalarTy);
llvm::PHINode *real = CGF.Builder.CreatePHI(scalarTy, 2);
real->addIncoming(callResult.first, callBB);
real->addIncoming(scalarZero, NullBB);
llvm::PHINode *imag = CGF.Builder.CreatePHI(scalarTy, 2);
imag->addIncoming(callResult.second, callBB);
imag->addIncoming(scalarZero, NullBB);
return RValue::getComplex(real, imag);
}
};
}
static llvm::Constant *getConstantGEP(llvm::LLVMContext &VMContext,
llvm::GlobalVariable *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->getValueType(), C, Idxs);
}
static bool hasObjCExceptionAttribute(ASTContext &Context,
const ObjCInterfaceDecl *OID) {
if (OID->hasAttr<ObjCExceptionAttr>())
return true;
if (const ObjCInterfaceDecl *Super = OID->getSuperClass())
return hasObjCExceptionAttribute(Context, Super);
return false;
}
static llvm::GlobalValue::LinkageTypes
getLinkageTypeForObjCMetadata(CodeGenModule &CGM, StringRef Section) {
if (CGM.getTriple().isOSBinFormatMachO() &&
(Section.empty() || Section.starts_with("__DATA")))
return llvm::GlobalValue::InternalLinkage;
return llvm::GlobalValue::PrivateLinkage;
}
static llvm::GlobalVariable *
finishAndCreateGlobal(ConstantInitBuilder::StructBuilder &Builder,
const llvm::Twine &Name, CodeGenModule &CGM) {
std::string SectionName;
if (CGM.getTriple().isOSBinFormatMachO())
SectionName = "__DATA, __objc_const";
auto *GV = Builder.finishAndCreateGlobal(
Name, CGM.getPointerAlign(), false,
getLinkageTypeForObjCMetadata(CGM, SectionName));
GV->setSection(SectionName);
return GV;
}
CGObjCMac::CGObjCMac(CodeGen::CodeGenModule &cgm) : CGObjCCommonMac(cgm),
ObjCTypes(cgm) {
ObjCABI = 1;
EmitImageInfo();
}
llvm::Value *CGObjCMac::GetClass(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) {
return EmitClassRef(CGF, ID);
}
llvm::Value *CGObjCMac::GetSelector(CodeGenFunction &CGF, Selector Sel) {
return EmitSelector(CGF, Sel);
}
Address CGObjCMac::GetAddrOfSelector(CodeGenFunction &CGF, Selector Sel) {
return EmitSelectorAddr(Sel);
}
llvm::Value *CGObjCMac::GetSelector(CodeGenFunction &CGF, const ObjCMethodDecl
*Method) {
return EmitSelector(CGF, Method->getSelector());
}
llvm::Constant *CGObjCMac::GetEHType(QualType T) {
if (T->isObjCIdType() ||
T->isObjCQualifiedIdType()) {
return CGM.GetAddrOfRTTIDescriptor(
CGM.getContext().getObjCIdRedefinitionType(), true);
}
if (T->isObjCClassType() ||
T->isObjCQualifiedClassType()) {
return CGM.GetAddrOfRTTIDescriptor(
CGM.getContext().getObjCClassRedefinitionType(), true);
}
if (T->isObjCObjectPointerType())
return CGM.GetAddrOfRTTIDescriptor(T, true);
llvm_unreachable("asking for catch type for ObjC type in fragile runtime");
}
struct __builtin_CFString {
const int *isa; // point to __CFConstantStringClassReference
int flags;
const char *str;
long length;
};
*/
struct __builtin_NSString {
const int *isa; // point to __NSConstantStringClassReference
const char *str;
unsigned int length;
};
*/
ConstantAddress
CGObjCCommonMac::GenerateConstantString(const StringLiteral *SL) {
return (!CGM.getLangOpts().NoConstantCFStrings
? CGM.GetAddrOfConstantCFString(SL)
: GenerateConstantNSString(SL));
}
static llvm::StringMapEntry<llvm::GlobalVariable *> &
GetConstantStringEntry(llvm::StringMap<llvm::GlobalVariable *> &Map,
const StringLiteral *Literal, unsigned &StringLength) {
StringRef String = Literal->getString();
StringLength = String.size();
return *Map.insert(std::make_pair(String, nullptr)).first;
}
llvm::Constant *CGObjCMac::getNSConstantStringClassRef() {
if (llvm::Value *V = ConstantStringClassRef)
return cast<llvm::Constant>(V);
auto &StringClass = CGM.getLangOpts().ObjCConstantStringClass;
std::string str =
StringClass.empty() ? "_NSConstantStringClassReference"
: "_" + StringClass + "ClassReference";
llvm::Type *PTy = llvm::ArrayType::get(CGM.IntTy, 0);
auto GV = CGM.CreateRuntimeVariable(PTy, str);
ConstantStringClassRef = GV;
return GV;
}
llvm::Constant *CGObjCNonFragileABIMac::getNSConstantStringClassRef() {
if (llvm::Value *V = ConstantStringClassRef)
return cast<llvm::Constant>(V);
auto &StringClass = CGM.getLangOpts().ObjCConstantStringClass;
std::string str =
StringClass.empty() ? "OBJC_CLASS_$_NSConstantString"
: "OBJC_CLASS_$_" + StringClass;
llvm::Constant *GV = GetClassGlobal(str, NotForDefinition);
ConstantStringClassRef = GV;
return GV;
}
ConstantAddress
CGObjCCommonMac::GenerateConstantNSString(const StringLiteral *Literal) {
unsigned StringLength = 0;
llvm::StringMapEntry<llvm::GlobalVariable *> &Entry =
GetConstantStringEntry(NSConstantStringMap, Literal, StringLength);
if (auto *C = Entry.second)
return ConstantAddress(
C, C->getValueType(), CharUnits::fromQuantity(C->getAlignment()));
llvm::Constant *Class = getNSConstantStringClassRef();
if (!NSConstantStringType) {
NSConstantStringType =
llvm::StructType::create({CGM.UnqualPtrTy, CGM.Int8PtrTy, CGM.IntTy},
"struct.__builtin_NSString");
}
ConstantInitBuilder Builder(CGM);
auto Fields = Builder.beginStruct(NSConstantStringType);
Fields.add(Class);
llvm::Constant *C =
llvm::ConstantDataArray::getString(VMContext, Entry.first());
llvm::GlobalValue::LinkageTypes Linkage = llvm::GlobalValue::PrivateLinkage;
bool isConstant = !CGM.getLangOpts().WritableStrings;
auto *GV = new llvm::GlobalVariable(CGM.getModule(), C->getType(), isConstant,
Linkage, C, ".str");
GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
GV->setAlignment(llvm::Align(1));
Fields.add(GV);
Fields.addInt(CGM.IntTy, StringLength);
CharUnits Alignment = CGM.getPointerAlign();
GV = Fields.finishAndCreateGlobal("_unnamed_nsstring_", Alignment,
true,
llvm::GlobalVariable::PrivateLinkage);
const char *NSStringSection = "__OBJC,__cstring_object,regular,no_dead_strip";
const char *NSStringNonFragileABISection =
"__DATA,__objc_stringobj,regular,no_dead_strip";
GV->setSection(CGM.getLangOpts().ObjCRuntime.isNonFragile()
? NSStringNonFragileABISection
: NSStringSection);
Entry.second = GV;
return ConstantAddress(GV, GV->getValueType(), Alignment);
}
enum {
kCFTaggedObjectID_Integer = (1 << 1) + 1
};
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) {
RawAddress ObjCSuper = CGF.CreateTempAlloca(
ObjCTypes.SuperTy, CGF.getPointerAlign(), "objc_super");
llvm::Value *ReceiverAsObject =
CGF.Builder.CreateBitCast(Receiver, ObjCTypes.ObjectPtrTy);
CGF.Builder.CreateStore(ReceiverAsObject,
CGF.Builder.CreateStructGEP(ObjCSuper, 0));
llvm::Type *ClassTyPtr = llvm::PointerType::getUnqual(ObjCTypes.ClassTy);
llvm::Value *Target;
if (IsClassMessage) {
if (isCategoryImpl) {
Target = EmitClassRef(CGF, Class->getSuperClass());
Target = CGF.Builder.CreateStructGEP(ObjCTypes.ClassTy, Target, 0);
Target = CGF.Builder.CreateAlignedLoad(ClassTyPtr, Target,
CGF.getPointerAlign());
} else {
llvm::Constant *MetaClassPtr = EmitMetaClassRef(Class);
llvm::Value *SuperPtr =
CGF.Builder.CreateStructGEP(ObjCTypes.ClassTy, MetaClassPtr, 1);
llvm::Value *Super = CGF.Builder.CreateAlignedLoad(ClassTyPtr, SuperPtr,
CGF.getPointerAlign());
Target = Super;
}
} else if (isCategoryImpl)
Target = EmitClassRef(CGF, Class->getSuperClass());
else {
llvm::Value *ClassPtr = EmitSuperClassRef(Class);
ClassPtr = CGF.Builder.CreateStructGEP(ObjCTypes.ClassTy, ClassPtr, 1);
Target = CGF.Builder.CreateAlignedLoad(ClassTyPtr, ClassPtr,
CGF.getPointerAlign());
}
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 EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(),
ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class,
ObjCTypes);
}
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 EmitMessageSend(CGF, Return, ResultType, Sel, Receiver,
CGF.getContext().getObjCIdType(), false, CallArgs,
Method, Class, ObjCTypes);
}
CodeGen::RValue
CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType,
Selector Sel,
llvm::Value *Arg0,
QualType Arg0Ty,
bool IsSuper,
const CallArgList &CallArgs,
const ObjCMethodDecl *Method,
const ObjCInterfaceDecl *ClassReceiver,
const ObjCCommonTypesHelper &ObjCTypes) {
CodeGenTypes &Types = CGM.getTypes();
auto selTy = CGF.getContext().getObjCSelType();
llvm::Value *SelValue = llvm::UndefValue::get(Types.ConvertType(selTy));
CallArgList ActualArgs;
if (!IsSuper)
Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy);
ActualArgs.add(RValue::get(Arg0), Arg0Ty);
if (!Method || !Method->isDirectMethod())
ActualArgs.add(RValue::get(SelValue), selTy);
ActualArgs.addFrom(CallArgs);
MessageSendInfo MSI = getMessageSendInfo(Method, ResultType, ActualArgs);
if (Method)
assert(CGM.getContext().getCanonicalType(Method->getReturnType()) ==
CGM.getContext().getCanonicalType(ResultType) &&
"Result type mismatch!");
bool ReceiverCanBeNull =
canMessageReceiverBeNull(CGF, Method, IsSuper, ClassReceiver, Arg0);
bool RequiresNullCheck = false;
bool RequiresSelValue = true;
llvm::FunctionCallee Fn = nullptr;
if (Method && Method->isDirectMethod()) {
assert(!IsSuper);
Fn = GenerateDirectMethod(Method, Method->getClassInterface());
RequiresSelValue = false;
} else if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) {
if (ReceiverCanBeNull) RequiresNullCheck = true;
Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper)
: ObjCTypes.getSendStretFn(IsSuper);
} else if (CGM.ReturnTypeUsesFPRet(ResultType)) {
Fn = (ObjCABI == 2) ? ObjCTypes.getSendFpretFn2(IsSuper)
: ObjCTypes.getSendFpretFn(IsSuper);
} else if (CGM.ReturnTypeUsesFP2Ret(ResultType)) {
Fn = (ObjCABI == 2) ? ObjCTypes.getSendFp2RetFn2(IsSuper)
: ObjCTypes.getSendFp2retFn(IsSuper);
} else {
if (ReceiverCanBeNull && CGM.ReturnTypeUsesSRet(MSI.CallInfo))
RequiresNullCheck = true;
Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
: ObjCTypes.getSendFn(IsSuper);
}
llvm::Constant *BitcastFn = cast<llvm::Constant>(
CGF.Builder.CreateBitCast(Fn.getCallee(), MSI.MessengerType));
if (Return.isUnused())
RequiresNullCheck = false;
if (!RequiresNullCheck && Method && Method->hasParamDestroyedInCallee())
RequiresNullCheck = true;
NullReturnState nullReturn;
if (RequiresNullCheck) {
nullReturn.init(CGF, Arg0);
}
if (RequiresSelValue) {
SelValue = GetSelector(CGF, Sel);
ActualArgs[1] = CallArg(RValue::get(SelValue), selTy);
}
llvm::CallBase *CallSite;
CGCallee Callee = CGCallee::forDirect(BitcastFn);
RValue rvalue = CGF.EmitCall(MSI.CallInfo, Callee, Return, ActualArgs,
&CallSite);
if (Method && Method->hasAttr<NoReturnAttr>() && !ReceiverCanBeNull) {
CallSite->setDoesNotReturn();
}
return nullReturn.complete(CGF, Return, rvalue, ResultType, CallArgs,
RequiresNullCheck ? Method : nullptr);
}
static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT,
bool pointee = false) {
if (FQT.isObjCGCStrong())
return Qualifiers::Strong;
if (FQT.isObjCGCWeak())
return Qualifiers::Weak;
if (auto ownership = FQT.getObjCLifetime()) {
if (pointee) return Qualifiers::GCNone;
switch (ownership) {
case Qualifiers::OCL_Weak: return Qualifiers::Weak;
case Qualifiers::OCL_Strong: return Qualifiers::Strong;
case Qualifiers::OCL_ExplicitNone: return Qualifiers::GCNone;
case Qualifiers::OCL_Autoreleasing: llvm_unreachable("autoreleasing ivar?");
case Qualifiers::OCL_None: llvm_unreachable("known nonzero");
}
llvm_unreachable("bad objc ownership");
}
if (FQT->isObjCObjectPointerType() || FQT->isBlockPointerType())
return Qualifiers::Strong;
if (Ctx.getLangOpts().getGC() != LangOptions::NonGC) {
if (const PointerType *PT = FQT->getAs<PointerType>())
return GetGCAttrTypeForType(Ctx, PT->getPointeeType(), true);
}
return Qualifiers::GCNone;
}
namespace {
struct IvarInfo {
CharUnits Offset;
uint64_t SizeInWords;
IvarInfo(CharUnits offset, uint64_t sizeInWords)
: Offset(offset), SizeInWords(sizeInWords) {}
bool operator<(const IvarInfo &other) const {
return Offset < other.Offset;
}
};
class IvarLayoutBuilder {
CodeGenModule &CGM;
CharUnits InstanceBegin;
CharUnits InstanceEnd;
bool ForStrongLayout;
bool IsDisordered = false;
llvm::SmallVector<IvarInfo, 8> IvarsInfo;
public:
IvarLayoutBuilder(CodeGenModule &CGM, CharUnits instanceBegin,
CharUnits instanceEnd, bool forStrongLayout)
: CGM(CGM), InstanceBegin(instanceBegin), InstanceEnd(instanceEnd),
ForStrongLayout(forStrongLayout) {
}
void visitRecord(const RecordType *RT, CharUnits offset);
template <class Iterator, class GetOffsetFn>
void visitAggregate(Iterator begin, Iterator end,
CharUnits aggrOffset,
const GetOffsetFn &getOffset);
void visitField(const FieldDecl *field, CharUnits offset);
void visitBlock(const CGBlockInfo &blockInfo);
bool hasBitmapData() const { return !IvarsInfo.empty(); }
llvm::Constant *buildBitmap(CGObjCCommonMac &CGObjC,
llvm::SmallVectorImpl<unsigned char> &buffer);
static void dump(ArrayRef<unsigned char> buffer) {
const unsigned char *s = buffer.data();
for (unsigned i = 0, e = buffer.size(); i < e; 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::Constant *CGObjCCommonMac::BuildGCBlockLayout(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
llvm::Constant *nullPtr = llvm::Constant::getNullValue(CGM.Int8PtrTy);
if (CGM.getLangOpts().getGC() == LangOptions::NonGC)
return nullPtr;
IvarLayoutBuilder builder(CGM, CharUnits::Zero(), blockInfo.BlockSize,
true);
builder.visitBlock(blockInfo);
if (!builder.hasBitmapData())
return nullPtr;
llvm::SmallVector<unsigned char, 32> buffer;
llvm::Constant *C = builder.buildBitmap(*this, buffer);
if (CGM.getLangOpts().ObjCGCBitmapPrint && !buffer.empty()) {
printf("\n block variable layout for block: ");
builder.dump(buffer);
}
return C;
}
void IvarLayoutBuilder::visitBlock(const CGBlockInfo &blockInfo) {
IvarsInfo.push_back(IvarInfo(CharUnits::Zero(), 1));
const BlockDecl *blockDecl = blockInfo.getBlockDecl();
CharUnits lastFieldOffset;
for (const auto &CI : blockDecl->captures()) {
const VarDecl *variable = CI.getVariable();
QualType type = variable->getType();
const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable);
if (capture.isConstant()) continue;
CharUnits fieldOffset = capture.getOffset();
if (fieldOffset < lastFieldOffset)
IsDisordered = true;
lastFieldOffset = fieldOffset;
if (CI.isByRef()) {
IvarsInfo.push_back(IvarInfo(fieldOffset, 1));
continue;
}
assert(!type->isArrayType() && "array variable should not be caught");
if (const RecordType *record = type->getAs<RecordType>()) {
visitRecord(record, fieldOffset);
continue;
}
Qualifiers::GC GCAttr = GetGCAttrTypeForType(CGM.getContext(), type);
if (GCAttr == Qualifiers::Strong) {
assert(CGM.getContext().getTypeSize(type) ==
CGM.getTarget().getPointerWidth(LangAS::Default));
IvarsInfo.push_back(IvarInfo(fieldOffset, 1));
}
}
}
Qualifiers::ObjCLifetime CGObjCCommonMac::getBlockCaptureLifetime(QualType FQT,
bool ByrefLayout) {
if (auto lifetime = FQT.getObjCLifetime())
return lifetime;
if (CGM.getLangOpts().ObjCAutoRefCount)
return Qualifiers::OCL_None;
if (FQT->isObjCObjectPointerType() || FQT->isBlockPointerType())
return ByrefLayout ? Qualifiers::OCL_ExplicitNone : Qualifiers::OCL_Strong;
return Qualifiers::OCL_None;
}
void CGObjCCommonMac::UpdateRunSkipBlockVars(bool IsByref,
Qualifiers::ObjCLifetime LifeTime,
CharUnits FieldOffset,
CharUnits FieldSize) {
if (IsByref)
RunSkipBlockVars.push_back(RUN_SKIP(BLOCK_LAYOUT_BYREF, FieldOffset,
FieldSize));
else if (LifeTime == Qualifiers::OCL_Strong)
RunSkipBlockVars.push_back(RUN_SKIP(BLOCK_LAYOUT_STRONG, FieldOffset,
FieldSize));
else if (LifeTime == Qualifiers::OCL_Weak)
RunSkipBlockVars.push_back(RUN_SKIP(BLOCK_LAYOUT_WEAK, FieldOffset,
FieldSize));
else if (LifeTime == Qualifiers::OCL_ExplicitNone)
RunSkipBlockVars.push_back(RUN_SKIP(BLOCK_LAYOUT_UNRETAINED, FieldOffset,
FieldSize));
else
RunSkipBlockVars.push_back(RUN_SKIP(BLOCK_LAYOUT_NON_OBJECT_BYTES,
FieldOffset,
FieldSize));
}
void CGObjCCommonMac::BuildRCRecordLayout(const llvm::StructLayout *RecLayout,
const RecordDecl *RD,
ArrayRef<const FieldDecl*> RecFields,
CharUnits BytePos, bool &HasUnion,
bool ByrefLayout) {
bool IsUnion = (RD && RD->isUnion());
CharUnits MaxUnionSize = CharUnits::Zero();
const FieldDecl *MaxField = nullptr;
const FieldDecl *LastFieldBitfieldOrUnnamed = nullptr;
CharUnits MaxFieldOffset = CharUnits::Zero();
CharUnits LastBitfieldOrUnnamedOffset = CharUnits::Zero();
if (RecFields.empty())
return;
unsigned ByteSizeInBits = CGM.getTarget().getCharWidth();
for (unsigned i = 0, e = RecFields.size(); i != e; ++i) {
const FieldDecl *Field = RecFields[i];
const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD);
CharUnits FieldOffset =
CGM.getContext().toCharUnitsFromBits(RL.getFieldOffset(i));
if (!Field->getIdentifier() || Field->isBitField()) {
LastFieldBitfieldOrUnnamed = Field;
LastBitfieldOrUnnamedOffset = FieldOffset;
continue;
}
LastFieldBitfieldOrUnnamed = nullptr;
QualType FQT = Field->getType();
if (FQT->isRecordType() || FQT->isUnionType()) {
if (FQT->isUnionType())
HasUnion = true;
BuildRCBlockVarRecordLayout(FQT->castAs<RecordType>(),
BytePos + FieldOffset, HasUnion);
continue;
}
if (const ArrayType *Array = CGM.getContext().getAsArrayType(FQT)) {
auto *CArray = cast<ConstantArrayType>(Array);
uint64_t ElCount = CArray->getZExtSize();
assert(CArray && "only array with known element size is supported");
FQT = CArray->getElementType();
while (const ArrayType *Array = CGM.getContext().getAsArrayType(FQT)) {
auto *CArray = cast<ConstantArrayType>(Array);
ElCount *= CArray->getZExtSize();
FQT = CArray->getElementType();
}
if (FQT->isRecordType() && ElCount) {
int OldIndex = RunSkipBlockVars.size() - 1;
auto *RT = FQT->castAs<RecordType>();
BuildRCBlockVarRecordLayout(RT, BytePos + FieldOffset, HasUnion);
uint64_t ElIx = 1;
for (int FirstIndex = RunSkipBlockVars.size() - 1 ;ElIx < ElCount; ElIx++) {
CharUnits Size = CGM.getContext().getTypeSizeInChars(RT);
for (int i = OldIndex+1; i <= FirstIndex; ++i)
RunSkipBlockVars.push_back(
RUN_SKIP(RunSkipBlockVars[i].opcode,
RunSkipBlockVars[i].block_var_bytepos + Size*ElIx,
RunSkipBlockVars[i].block_var_size));
}
continue;
}
}
CharUnits FieldSize = CGM.getContext().getTypeSizeInChars(Field->getType());
if (IsUnion) {
CharUnits UnionIvarSize = FieldSize;
if (UnionIvarSize > MaxUnionSize) {
MaxUnionSize = UnionIvarSize;
MaxField = Field;
MaxFieldOffset = FieldOffset;
}
} else {
UpdateRunSkipBlockVars(false,
getBlockCaptureLifetime(FQT, ByrefLayout),
BytePos + FieldOffset,
FieldSize);
}
}
if (LastFieldBitfieldOrUnnamed) {
if (LastFieldBitfieldOrUnnamed->isBitField()) {
uint64_t BitFieldSize
= LastFieldBitfieldOrUnnamed->getBitWidthValue(CGM.getContext());
unsigned UnsSize = (BitFieldSize / ByteSizeInBits) +
((BitFieldSize % ByteSizeInBits) != 0);
CharUnits Size = CharUnits::fromQuantity(UnsSize);
Size += LastBitfieldOrUnnamedOffset;
UpdateRunSkipBlockVars(false,
getBlockCaptureLifetime(LastFieldBitfieldOrUnnamed->getType(),
ByrefLayout),
BytePos + LastBitfieldOrUnnamedOffset,
Size);
} else {
assert(!LastFieldBitfieldOrUnnamed->getIdentifier() &&"Expected unnamed");
CharUnits FieldSize
= CGM.getContext().getTypeSizeInChars(LastFieldBitfieldOrUnnamed->getType());
UpdateRunSkipBlockVars(false,
getBlockCaptureLifetime(LastFieldBitfieldOrUnnamed->getType(),
ByrefLayout),
BytePos + LastBitfieldOrUnnamedOffset,
FieldSize);
}
}
if (MaxField)
UpdateRunSkipBlockVars(false,
getBlockCaptureLifetime(MaxField->getType(), ByrefLayout),
BytePos + MaxFieldOffset,
MaxUnionSize);
}
void CGObjCCommonMac::BuildRCBlockVarRecordLayout(const RecordType *RT,
CharUnits BytePos,
bool &HasUnion,
bool ByrefLayout) {
const RecordDecl *RD = RT->getDecl();
SmallVector<const FieldDecl*, 16> Fields(RD->fields());
llvm::Type *Ty = CGM.getTypes().ConvertType(QualType(RT, 0));
const llvm::StructLayout *RecLayout =
CGM.getDataLayout().getStructLayout(cast<llvm::StructType>(Ty));
BuildRCRecordLayout(RecLayout, RD, Fields, BytePos, HasUnion, ByrefLayout);
}
uint64_t CGObjCCommonMac::InlineLayoutInstruction(
SmallVectorImpl<unsigned char> &Layout) {
uint64_t Result = 0;
if (Layout.size() <= 3) {
unsigned size = Layout.size();
unsigned strong_word_count = 0, byref_word_count=0, weak_word_count=0;
unsigned char inst;
enum BLOCK_LAYOUT_OPCODE opcode ;
switch (size) {
case 3:
inst = Layout[0];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_STRONG)
strong_word_count = (inst & 0xF)+1;
else
return 0;
inst = Layout[1];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_BYREF)
byref_word_count = (inst & 0xF)+1;
else
return 0;
inst = Layout[2];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_WEAK)
weak_word_count = (inst & 0xF)+1;
else
return 0;
break;
case 2:
inst = Layout[0];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_STRONG) {
strong_word_count = (inst & 0xF)+1;
inst = Layout[1];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_BYREF)
byref_word_count = (inst & 0xF)+1;
else if (opcode == BLOCK_LAYOUT_WEAK)
weak_word_count = (inst & 0xF)+1;
else
return 0;
}
else if (opcode == BLOCK_LAYOUT_BYREF) {
byref_word_count = (inst & 0xF)+1;
inst = Layout[1];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_WEAK)
weak_word_count = (inst & 0xF)+1;
else
return 0;
}
else
return 0;
break;
case 1:
inst = Layout[0];
opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_STRONG)
strong_word_count = (inst & 0xF)+1;
else if (opcode == BLOCK_LAYOUT_BYREF)
byref_word_count = (inst & 0xF)+1;
else if (opcode == BLOCK_LAYOUT_WEAK)
weak_word_count = (inst & 0xF)+1;
else
return 0;
break;
default:
return 0;
}
if (strong_word_count == 16 || byref_word_count == 16 || weak_word_count == 16)
return 0;
unsigned count =
(strong_word_count != 0) + (byref_word_count != 0) + (weak_word_count != 0);
if (size == count) {
if (strong_word_count)
Result = strong_word_count;
Result <<= 4;
if (byref_word_count)
Result += byref_word_count;
Result <<= 4;
if (weak_word_count)
Result += weak_word_count;
}
}
return Result;
}
llvm::Constant *CGObjCCommonMac::getBitmapBlockLayout(bool ComputeByrefLayout) {
llvm::Constant *nullPtr = llvm::Constant::getNullValue(CGM.Int8PtrTy);
if (RunSkipBlockVars.empty())
return nullPtr;
unsigned WordSizeInBits = CGM.getTarget().getPointerWidth(LangAS::Default);
unsigned ByteSizeInBits = CGM.getTarget().getCharWidth();
unsigned WordSizeInBytes = WordSizeInBits/ByteSizeInBits;
llvm::array_pod_sort(RunSkipBlockVars.begin(), RunSkipBlockVars.end());
SmallVector<unsigned char, 16> Layout;
unsigned size = RunSkipBlockVars.size();
for (unsigned i = 0; i < size; i++) {
enum BLOCK_LAYOUT_OPCODE opcode = RunSkipBlockVars[i].opcode;
CharUnits start_byte_pos = RunSkipBlockVars[i].block_var_bytepos;
CharUnits end_byte_pos = start_byte_pos;
unsigned j = i+1;
while (j < size) {
if (opcode == RunSkipBlockVars[j].opcode) {
end_byte_pos = RunSkipBlockVars[j++].block_var_bytepos;
i++;
}
else
break;
}
CharUnits size_in_bytes =
end_byte_pos - start_byte_pos + RunSkipBlockVars[j-1].block_var_size;
if (j < size) {
CharUnits gap =
RunSkipBlockVars[j].block_var_bytepos -
RunSkipBlockVars[j-1].block_var_bytepos - RunSkipBlockVars[j-1].block_var_size;
size_in_bytes += gap;
}
CharUnits residue_in_bytes = CharUnits::Zero();
if (opcode == BLOCK_LAYOUT_NON_OBJECT_BYTES) {
residue_in_bytes = size_in_bytes % WordSizeInBytes;
size_in_bytes -= residue_in_bytes;
opcode = BLOCK_LAYOUT_NON_OBJECT_WORDS;
}
unsigned size_in_words = size_in_bytes.getQuantity() / WordSizeInBytes;
while (size_in_words >= 16) {
unsigned char inst = (opcode << 4) | 0xf;
Layout.push_back(inst);
size_in_words -= 16;
}
if (size_in_words > 0) {
unsigned char inst = (opcode << 4) | (size_in_words-1);
Layout.push_back(inst);
}
if (residue_in_bytes > CharUnits::Zero()) {
unsigned char inst =
(BLOCK_LAYOUT_NON_OBJECT_BYTES << 4) | (residue_in_bytes.getQuantity()-1);
Layout.push_back(inst);
}
}
while (!Layout.empty()) {
unsigned char inst = Layout.back();
enum BLOCK_LAYOUT_OPCODE opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
if (opcode == BLOCK_LAYOUT_NON_OBJECT_BYTES || opcode == BLOCK_LAYOUT_NON_OBJECT_WORDS)
Layout.pop_back();
else
break;
}
uint64_t Result = InlineLayoutInstruction(Layout);
if (Result != 0) {
if (CGM.getLangOpts().ObjCGCBitmapPrint) {
if (ComputeByrefLayout)
printf("\n Inline BYREF variable layout: ");
else
printf("\n Inline block variable layout: ");
printf("0x0%" PRIx64 "", Result);
if (auto numStrong = (Result & 0xF00) >> 8)
printf(", BL_STRONG:%d", (int) numStrong);
if (auto numByref = (Result & 0x0F0) >> 4)
printf(", BL_BYREF:%d", (int) numByref);
if (auto numWeak = (Result & 0x00F) >> 0)
printf(", BL_WEAK:%d", (int) numWeak);
printf(", BL_OPERATOR:0\n");
}
return llvm::ConstantInt::get(CGM.IntPtrTy, Result);
}
unsigned char inst = (BLOCK_LAYOUT_OPERATOR << 4) | 0;
Layout.push_back(inst);
std::string BitMap;
for (unsigned i = 0, e = Layout.size(); i != e; i++)
BitMap += Layout[i];
if (CGM.getLangOpts().ObjCGCBitmapPrint) {
if (ComputeByrefLayout)
printf("\n Byref variable layout: ");
else
printf("\n Block variable layout: ");
for (unsigned i = 0, e = BitMap.size(); i != e; i++) {
unsigned char inst = BitMap[i];
enum BLOCK_LAYOUT_OPCODE opcode = (enum BLOCK_LAYOUT_OPCODE) (inst >> 4);
unsigned delta = 1;
switch (opcode) {
case BLOCK_LAYOUT_OPERATOR:
printf("BL_OPERATOR:");
delta = 0;
break;
case BLOCK_LAYOUT_NON_OBJECT_BYTES:
printf("BL_NON_OBJECT_BYTES:");
break;
case BLOCK_LAYOUT_NON_OBJECT_WORDS:
printf("BL_NON_OBJECT_WORD:");
break;
case BLOCK_LAYOUT_STRONG:
printf("BL_STRONG:");
break;
case BLOCK_LAYOUT_BYREF:
printf("BL_BYREF:");
break;
case BLOCK_LAYOUT_WEAK:
printf("BL_WEAK:");
break;
case BLOCK_LAYOUT_UNRETAINED:
printf("BL_UNRETAINED:");
break;
}
printf("%d", (inst & 0xf) + delta);
if (i < e-1)
printf(", ");
else
printf("\n");
}
}
auto *Entry = CreateCStringLiteral(BitMap, ObjCLabelType::ClassName,
true,
false);
return getConstantGEP(VMContext, Entry, 0, 0);
}
static std::string getBlockLayoutInfoString(
const SmallVectorImpl<CGObjCCommonMac::RUN_SKIP> &RunSkipBlockVars,
bool HasCopyDisposeHelpers) {
std::string Str;
for (const CGObjCCommonMac::RUN_SKIP &R : RunSkipBlockVars) {
if (R.opcode == CGObjCCommonMac::BLOCK_LAYOUT_UNRETAINED) {
Str += "u";
} else if (HasCopyDisposeHelpers) {
continue;
} else {
switch (R.opcode) {
case CGObjCCommonMac::BLOCK_LAYOUT_STRONG:
Str += "s";
break;
case CGObjCCommonMac::BLOCK_LAYOUT_BYREF:
Str += "r";
break;
case CGObjCCommonMac::BLOCK_LAYOUT_WEAK:
Str += "w";
break;
default:
continue;
}
}
Str += llvm::to_string(R.block_var_bytepos.getQuantity());
Str += "l" + llvm::to_string(R.block_var_size.getQuantity());
}
return Str;
}
void CGObjCCommonMac::fillRunSkipBlockVars(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
assert(CGM.getLangOpts().getGC() == LangOptions::NonGC);
RunSkipBlockVars.clear();
bool hasUnion = false;
unsigned WordSizeInBits = CGM.getTarget().getPointerWidth(LangAS::Default);
unsigned ByteSizeInBits = CGM.getTarget().getCharWidth();
unsigned WordSizeInBytes = WordSizeInBits/ByteSizeInBits;
const BlockDecl *blockDecl = blockInfo.getBlockDecl();
const llvm::StructLayout *layout =
CGM.getDataLayout().getStructLayout(blockInfo.StructureType);
if (blockInfo.BlockHeaderForcedGapSize != CharUnits::Zero())
UpdateRunSkipBlockVars(false, Qualifiers::OCL_None,
blockInfo.BlockHeaderForcedGapOffset,
blockInfo.BlockHeaderForcedGapSize);
for (const auto &CI : blockDecl->captures()) {
const VarDecl *variable = CI.getVariable();
QualType type = variable->getType();
const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable);
if (capture.isConstant()) continue;
CharUnits fieldOffset =
CharUnits::fromQuantity(layout->getElementOffset(capture.getIndex()));
assert(!type->isArrayType() && "array variable should not be caught");
if (!CI.isByRef())
if (const RecordType *record = type->getAs<RecordType>()) {
BuildRCBlockVarRecordLayout(record, fieldOffset, hasUnion);
continue;
}
CharUnits fieldSize;
if (CI.isByRef())
fieldSize = CharUnits::fromQuantity(WordSizeInBytes);
else
fieldSize = CGM.getContext().getTypeSizeInChars(type);
UpdateRunSkipBlockVars(CI.isByRef(), getBlockCaptureLifetime(type, false),
fieldOffset, fieldSize);
}
}
llvm::Constant *
CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
fillRunSkipBlockVars(CGM, blockInfo);
return getBitmapBlockLayout(false);
}
std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
fillRunSkipBlockVars(CGM, blockInfo);
return getBlockLayoutInfoString(RunSkipBlockVars, blockInfo.NeedsCopyDispose);
}
llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM,
QualType T) {
assert(CGM.getLangOpts().getGC() == LangOptions::NonGC);
assert(!T->isArrayType() && "__block array variable should not be caught");
CharUnits fieldOffset;
RunSkipBlockVars.clear();
bool hasUnion = false;
if (const RecordType *record = T->getAs<RecordType>()) {
BuildRCBlockVarRecordLayout(record, fieldOffset, hasUnion, true );
llvm::Constant *Result = getBitmapBlockLayout(true);
if (isa<llvm::ConstantInt>(Result))
Result = llvm::ConstantExpr::getIntToPtr(Result, CGM.Int8PtrTy);
return Result;
}
llvm::Constant *nullPtr = llvm::Constant::getNullValue(CGM.Int8PtrTy);
return nullPtr;
}
llvm::Value *CGObjCMac::GenerateProtocolRef(CodeGenFunction &CGF,
const ObjCProtocolDecl *PD) {
LazySymbols.insert(&CGM.getContext().Idents.get("Protocol"));
return GetProtocolRef(PD);
}
void CGObjCCommonMac::GenerateProtocol(const ObjCProtocolDecl *PD) {
DefinedProtocols.insert(PD->getIdentifier());
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);
}
llvm::Value *CGObjCCommonMac::EmitClassRefViaRuntime(
CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID,
ObjCCommonTypesHelper &ObjCTypes) {
llvm::FunctionCallee lookUpClassFn = ObjCTypes.getLookUpClassFn();
llvm::Value *className = CGF.CGM
.GetAddrOfConstantCString(std::string(
ID->getObjCRuntimeNameAsString()))
.getPointer();
ASTContext &ctx = CGF.CGM.getContext();
className =
CGF.Builder.CreateBitCast(className,
CGF.ConvertType(
ctx.getPointerType(ctx.CharTy.withConst())));
llvm::CallInst *call = CGF.Builder.CreateCall(lookUpClassFn, className);
call->setDoesNotThrow();
return call;
}
// 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()];
if (Entry && Entry->hasInitializer())
return Entry;
if (const ObjCProtocolDecl *Def = PD->getDefinition())
PD = Def;
LazySymbols.insert(&CGM.getContext().Idents.get("Protocol"));
auto methodLists = ProtocolMethodLists::get(PD);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ProtocolTy);
values.add(EmitProtocolExtension(PD, methodLists));
values.add(GetClassName(PD->getObjCRuntimeNameAsString()));
values.add(EmitProtocolList("OBJC_PROTOCOL_REFS_" + PD->getName(),
PD->protocol_begin(), PD->protocol_end()));
values.add(methodLists.emitMethodList(this, PD,
ProtocolMethodLists::RequiredInstanceMethods));
values.add(methodLists.emitMethodList(this, PD,
ProtocolMethodLists::RequiredClassMethods));
if (Entry) {
assert(Entry->hasPrivateLinkage());
values.finishAndSetAsInitializer(Entry);
} else {
Entry = values.finishAndCreateGlobal("OBJC_PROTOCOL_" + PD->getName(),
CGM.getPointerAlign(),
false,
llvm::GlobalValue::PrivateLinkage);
Entry->setSection("__OBJC,__protocol,regular,no_dead_strip");
Protocols[PD->getIdentifier()] = Entry;
}
CGM.addCompilerUsedGlobal(Entry);
return Entry;
}
llvm::Constant *CGObjCMac::GetOrEmitProtocolRef(const ObjCProtocolDecl *PD) {
llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()];
if (!Entry) {
Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolTy,
false, llvm::GlobalValue::PrivateLinkage,
nullptr, "OBJC_PROTOCOL_" + PD->getName());
Entry->setSection("__OBJC,__protocol,regular,no_dead_strip");
Entry->setAlignment(llvm::Align(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;
const char ** extendedMethodTypes;
struct objc_property_list *class_properties;
};
*/
llvm::Constant *
CGObjCMac::EmitProtocolExtension(const ObjCProtocolDecl *PD,
const ProtocolMethodLists &methodLists) {
auto optInstanceMethods =
methodLists.emitMethodList(this, PD,
ProtocolMethodLists::OptionalInstanceMethods);
auto optClassMethods =
methodLists.emitMethodList(this, PD,
ProtocolMethodLists::OptionalClassMethods);
auto extendedMethodTypes =
EmitProtocolMethodTypes("OBJC_PROTOCOL_METHOD_TYPES_" + PD->getName(),
methodLists.emitExtendedTypesArray(this),
ObjCTypes);
auto instanceProperties =
EmitPropertyList("OBJC_$_PROP_PROTO_LIST_" + PD->getName(), nullptr, PD,
ObjCTypes, false);
auto classProperties =
EmitPropertyList("OBJC_$_CLASS_PROP_PROTO_LIST_" + PD->getName(), nullptr,
PD, ObjCTypes, true);
if (optInstanceMethods->isNullValue() &&
optClassMethods->isNullValue() &&
extendedMethodTypes->isNullValue() &&
instanceProperties->isNullValue() &&
classProperties->isNullValue()) {
return llvm::Constant::getNullValue(ObjCTypes.ProtocolExtensionPtrTy);
}
uint64_t size =
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ProtocolExtensionTy);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ProtocolExtensionTy);
values.addInt(ObjCTypes.IntTy, size);
values.add(optInstanceMethods);
values.add(optClassMethods);
values.add(instanceProperties);
values.add(extendedMethodTypes);
values.add(classProperties);
return CreateMetadataVar("_OBJC_PROTOCOLEXT_" + PD->getName(), values,
StringRef(), CGM.getPointerAlign(), true);
}
struct objc_protocol_list {
struct objc_protocol_list *next;
long count;
Protocol *list[];
};
*/
llvm::Constant *
CGObjCMac::EmitProtocolList(Twine name,
ObjCProtocolDecl::protocol_iterator begin,
ObjCProtocolDecl::protocol_iterator end) {
auto PDs = GetRuntimeProtocolList(begin, end);
if (PDs.empty())
return llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
values.addNullPointer(ObjCTypes.ProtocolListPtrTy);
auto countSlot = values.addPlaceholder();
auto refsArray = values.beginArray(ObjCTypes.ProtocolPtrTy);
for (const auto *Proto : PDs)
refsArray.add(GetProtocolRef(Proto));
auto count = refsArray.size();
refsArray.addNullPointer(ObjCTypes.ProtocolPtrTy);
refsArray.finishAndAddTo(values);
values.fillPlaceholderWithInt(countSlot, ObjCTypes.LongTy, count);
StringRef section;
if (CGM.getTriple().isOSBinFormatMachO())
section = "__OBJC,__cat_cls_meth,regular,no_dead_strip";
llvm::GlobalVariable *GV =
CreateMetadataVar(name, values, section, CGM.getPointerAlign(), false);
return GV;
}
static void
PushProtocolProperties(llvm::SmallPtrSet<const IdentifierInfo*,16> &PropertySet,
SmallVectorImpl<const ObjCPropertyDecl *> &Properties,
const ObjCProtocolDecl *Proto,
bool IsClassProperty) {
for (const auto *PD : Proto->properties()) {
if (IsClassProperty != PD->isClassProperty())
continue;
if (!PropertySet.insert(PD->getIdentifier()).second)
continue;
Properties.push_back(PD);
}
for (const auto *P : Proto->protocols())
PushProtocolProperties(PropertySet, Properties, P, IsClassProperty);
}
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(Twine Name,
const Decl *Container,
const ObjCContainerDecl *OCD,
const ObjCCommonTypesHelper &ObjCTypes,
bool IsClassProperty) {
if (IsClassProperty) {
const llvm::Triple &Triple = CGM.getTarget().getTriple();
if ((Triple.isMacOSX() && Triple.isMacOSXVersionLT(10, 11)) ||
(Triple.isiOS() && Triple.isOSVersionLT(9)))
return llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy);
}
SmallVector<const ObjCPropertyDecl *, 16> Properties;
llvm::SmallPtrSet<const IdentifierInfo*, 16> PropertySet;
if (const ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(OCD))
for (const ObjCCategoryDecl *ClassExt : OID->known_extensions())
for (auto *PD : ClassExt->properties()) {
if (IsClassProperty != PD->isClassProperty())
continue;
if (PD->isDirectProperty())
continue;
PropertySet.insert(PD->getIdentifier());
Properties.push_back(PD);
}
for (const auto *PD : OCD->properties()) {
if (IsClassProperty != PD->isClassProperty())
continue;
if (!PropertySet.insert(PD->getIdentifier()).second)
continue;
if (PD->isDirectProperty())
continue;
Properties.push_back(PD);
}
if (const ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(OCD)) {
for (const auto *P : OID->all_referenced_protocols())
PushProtocolProperties(PropertySet, Properties, P, IsClassProperty);
}
else if (const ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(OCD)) {
for (const auto *P : CD->protocols())
PushProtocolProperties(PropertySet, Properties, P, IsClassProperty);
}
if (Properties.empty())
return llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy);
unsigned propertySize =
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.PropertyTy);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
values.addInt(ObjCTypes.IntTy, propertySize);
values.addInt(ObjCTypes.IntTy, Properties.size());
auto propertiesArray = values.beginArray(ObjCTypes.PropertyTy);
for (auto PD : Properties) {
auto property = propertiesArray.beginStruct(ObjCTypes.PropertyTy);
property.add(GetPropertyName(PD->getIdentifier()));
property.add(GetPropertyTypeString(PD, Container));
property.finishAndAddTo(propertiesArray);
}
propertiesArray.finishAndAddTo(values);
StringRef Section;
if (CGM.getTriple().isOSBinFormatMachO())
Section = (ObjCABI == 2) ? "__DATA, __objc_const"
: "__OBJC,__property,regular,no_dead_strip";
llvm::GlobalVariable *GV =
CreateMetadataVar(Name, values, Section, CGM.getPointerAlign(), true);
return GV;
}
llvm::Constant *
CGObjCCommonMac::EmitProtocolMethodTypes(Twine Name,
ArrayRef<llvm::Constant*> MethodTypes,
const ObjCCommonTypesHelper &ObjCTypes) {
if (MethodTypes.empty())
return llvm::Constant::getNullValue(ObjCTypes.Int8PtrPtrTy);
llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.Int8PtrTy,
MethodTypes.size());
llvm::Constant *Init = llvm::ConstantArray::get(AT, MethodTypes);
StringRef Section;
if (CGM.getTriple().isOSBinFormatMachO() && ObjCABI == 2)
Section = "__DATA, __objc_const";
llvm::GlobalVariable *GV =
CreateMetadataVar(Name, Init, Section, CGM.getPointerAlign(), true);
return GV;
}
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; // sizeof(struct _objc_category)
struct _objc_property_list *instance_properties;
struct _objc_property_list *class_properties;
};
*/
void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) {
unsigned Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.CategoryTy);
const ObjCInterfaceDecl *Interface = OCD->getClassInterface();
const ObjCCategoryDecl *Category =
Interface->FindCategoryDeclaration(OCD->getIdentifier());
SmallString<256> ExtName;
llvm::raw_svector_ostream(ExtName) << Interface->getName() << '_'
<< OCD->getName();
ConstantInitBuilder Builder(CGM);
auto Values = Builder.beginStruct(ObjCTypes.CategoryTy);
enum {
InstanceMethods,
ClassMethods,
NumMethodLists
};
SmallVector<const ObjCMethodDecl *, 16> Methods[NumMethodLists];
for (const auto *MD : OCD->methods()) {
if (!MD->isDirectMethod())
Methods[unsigned(MD->isClassMethod())].push_back(MD);
}
Values.add(GetClassName(OCD->getName()));
Values.add(GetClassName(Interface->getObjCRuntimeNameAsString()));
LazySymbols.insert(Interface->getIdentifier());
Values.add(emitMethodList(ExtName, MethodListType::CategoryInstanceMethods,
Methods[InstanceMethods]));
Values.add(emitMethodList(ExtName, MethodListType::CategoryClassMethods,
Methods[ClassMethods]));
if (Category) {
Values.add(
EmitProtocolList("OBJC_CATEGORY_PROTOCOLS_" + ExtName.str(),
Category->protocol_begin(), Category->protocol_end()));
} else {
Values.addNullPointer(ObjCTypes.ProtocolListPtrTy);
}
Values.addInt(ObjCTypes.IntTy, Size);
if (Category) {
Values.add(EmitPropertyList("_OBJC_$_PROP_LIST_" + ExtName.str(),
OCD, Category, ObjCTypes, false));
Values.add(EmitPropertyList("_OBJC_$_CLASS_PROP_LIST_" + ExtName.str(),
OCD, Category, ObjCTypes, true));
} else {
Values.addNullPointer(ObjCTypes.PropertyListPtrTy);
Values.addNullPointer(ObjCTypes.PropertyListPtrTy);
}
llvm::GlobalVariable *GV =
CreateMetadataVar("OBJC_CATEGORY_" + ExtName.str(), Values,
"__OBJC,__category,regular,no_dead_strip",
CGM.getPointerAlign(), true);
DefinedCategories.push_back(GV);
DefinedCategoryNames.insert(llvm::CachedHashString(ExtName));
MethodDefinitions.clear();
}
enum FragileClassFlags {
FragileABI_Class_Factory = 0x00001,
FragileABI_Class_Meta = 0x00002,
FragileABI_Class_HasCXXStructors = 0x02000,
FragileABI_Class_Hidden = 0x20000,
FragileABI_Class_CompiledByARC = 0x04000000,
FragileABI_Class_HasMRCWeakIvars = 0x08000000,
};
enum NonFragileClassFlags {
NonFragileABI_Class_Meta = 0x00001,
NonFragileABI_Class_Root = 0x00002,
NonFragileABI_Class_HasCXXStructors = 0x00004,
NonFragileABI_Class_Hidden = 0x00010,
NonFragileABI_Class_Exception = 0x00020,
NonFragileABI_Class_HasIvarReleaser = 0x00040,
NonFragileABI_Class_CompiledByARC = 0x00080,
NonFragileABI_Class_HasCXXDestructorOnly = 0x00100,
NonFragileABI_Class_HasMRCWeakIvars = 0x00200,
};
static bool hasWeakMember(QualType type) {
if (type.getObjCLifetime() == Qualifiers::OCL_Weak) {
return true;
}
if (auto recType = type->getAs<RecordType>()) {
for (auto *field : recType->getDecl()->fields()) {
if (hasWeakMember(field->getType()))
return true;
}
}
return false;
}
static bool hasMRCWeakIvars(CodeGenModule &CGM,
const ObjCImplementationDecl *ID) {
if (!CGM.getLangOpts().ObjCWeak) return false;
assert(CGM.getLangOpts().getGC() == LangOptions::NonGC);
for (const ObjCIvarDecl *ivar =
ID->getClassInterface()->all_declared_ivar_begin();
ivar; ivar = ivar->getNextIvar()) {
if (hasWeakMember(ivar->getType()))
return true;
}
return false;
}
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 (<rdr://4585769>)
const char *ivar_layout;
struct _objc_class_ext *ext;
};
See EmitClassExtension();
*/
void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) {
IdentifierInfo *RuntimeName =
&CGM.getContext().Idents.get(ID->getObjCRuntimeNameAsString());
DefinedSymbols.insert(RuntimeName);
std::string ClassName = ID->getNameAsString();
ObjCInterfaceDecl *Interface =
const_cast<ObjCInterfaceDecl*>(ID->getClassInterface());
llvm::Constant *Protocols =
EmitProtocolList("OBJC_CLASS_PROTOCOLS_" + ID->getName(),
Interface->all_referenced_protocol_begin(),
Interface->all_referenced_protocol_end());
unsigned Flags = FragileABI_Class_Factory;
if (ID->hasNonZeroConstructors() || ID->hasDestructors())
Flags |= FragileABI_Class_HasCXXStructors;
bool hasMRCWeak = false;
if (CGM.getLangOpts().ObjCAutoRefCount)
Flags |= FragileABI_Class_CompiledByARC;
else if ((hasMRCWeak = hasMRCWeakIvars(CGM, ID)))
Flags |= FragileABI_Class_HasMRCWeakIvars;
CharUnits Size =
CGM.getContext().getASTObjCImplementationLayout(ID).getSize();
if (ID->getClassInterface()->getVisibility() == HiddenVisibility)
Flags |= FragileABI_Class_Hidden;
enum {
InstanceMethods,
ClassMethods,
NumMethodLists
};
SmallVector<const ObjCMethodDecl *, 16> Methods[NumMethodLists];
for (const auto *MD : ID->methods()) {
if (!MD->isDirectMethod())
Methods[unsigned(MD->isClassMethod())].push_back(MD);
}
for (const auto *PID : ID->property_impls()) {
if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) {
if (PID->getPropertyDecl()->isDirectProperty())
continue;
if (ObjCMethodDecl *MD = PID->getGetterMethodDecl())
if (GetMethodDefinition(MD))
Methods[InstanceMethods].push_back(MD);
if (ObjCMethodDecl *MD = PID->getSetterMethodDecl())
if (GetMethodDefinition(MD))
Methods[InstanceMethods].push_back(MD);
}
}
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ClassTy);
values.add(EmitMetaClass(ID, Protocols, Methods[ClassMethods]));
if (ObjCInterfaceDecl *Super = Interface->getSuperClass()) {
LazySymbols.insert(Super->getIdentifier());
values.add(GetClassName(Super->getObjCRuntimeNameAsString()));
} else {
values.addNullPointer(ObjCTypes.ClassPtrTy);
}
values.add(GetClassName(ID->getObjCRuntimeNameAsString()));
values.addInt(ObjCTypes.LongTy, 0);
values.addInt(ObjCTypes.LongTy, Flags);
values.addInt(ObjCTypes.LongTy, Size.getQuantity());
values.add(EmitIvarList(ID, false));
values.add(emitMethodList(ID->getName(), MethodListType::InstanceMethods,
Methods[InstanceMethods]));
values.addNullPointer(ObjCTypes.CachePtrTy);
values.add(Protocols);
values.add(BuildStrongIvarLayout(ID, CharUnits::Zero(), Size));
values.add(EmitClassExtension(ID, Size, hasMRCWeak,
false));
std::string Name("OBJC_CLASS_");
Name += ClassName;
const char *Section = "__OBJC,__class,regular,no_dead_strip";
llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name, true);
if (GV) {
assert(GV->getValueType() == ObjCTypes.ClassTy &&
"Forward metaclass reference has incorrect type.");
values.finishAndSetAsInitializer(GV);
GV->setSection(Section);
GV->setAlignment(CGM.getPointerAlign().getAsAlign());
CGM.addCompilerUsedGlobal(GV);
} else
GV = CreateMetadataVar(Name, values, Section, CGM.getPointerAlign(), true);
DefinedClasses.push_back(GV);
ImplementedClasses.push_back(Interface);
MethodDefinitions.clear();
}
llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID,
llvm::Constant *Protocols,
ArrayRef<const ObjCMethodDecl*> Methods) {
unsigned Flags = FragileABI_Class_Meta;
unsigned Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ClassTy);
if (ID->getClassInterface()->getVisibility() == HiddenVisibility)
Flags |= FragileABI_Class_Hidden;
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ClassTy);
const ObjCInterfaceDecl *Root = ID->getClassInterface();
while (const ObjCInterfaceDecl *Super = Root->getSuperClass())
Root = Super;
values.add(GetClassName(Root->getObjCRuntimeNameAsString()));
if (ObjCInterfaceDecl *Super = ID->getClassInterface()->getSuperClass()) {
values.add(GetClassName(Super->getObjCRuntimeNameAsString()));
} else {
values.addNullPointer(ObjCTypes.ClassPtrTy);
}
values.add(GetClassName(ID->getObjCRuntimeNameAsString()));
values.addInt(ObjCTypes.LongTy, 0);
values.addInt(ObjCTypes.LongTy, Flags);
values.addInt(ObjCTypes.LongTy, Size);
values.add(EmitIvarList(ID, true));
values.add(emitMethodList(ID->getName(), MethodListType::ClassMethods,
Methods));
values.addNullPointer(ObjCTypes.CachePtrTy);
values.add(Protocols);
values.addNullPointer(ObjCTypes.Int8PtrTy);
values.add(EmitClassExtension(ID, CharUnits::Zero(), false,
true));
std::string Name("OBJC_METACLASS_");
Name += ID->getName();
llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name, true);
if (GV) {
assert(GV->getValueType() == ObjCTypes.ClassTy &&
"Forward metaclass reference has incorrect type.");
values.finishAndSetAsInitializer(GV);
} else {
GV = values.finishAndCreateGlobal(Name, CGM.getPointerAlign(),
false,
llvm::GlobalValue::PrivateLinkage);
}
GV->setSection("__OBJC,__meta_class,regular,no_dead_strip");
CGM.addCompilerUsedGlobal(GV);
return GV;
}
llvm::Constant *CGObjCMac::EmitMetaClassRef(const ObjCInterfaceDecl *ID) {
std::string Name = "OBJC_METACLASS_" + ID->getNameAsString();
llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name, true);
if (!GV)
GV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassTy, false,
llvm::GlobalValue::PrivateLinkage, nullptr,
Name);
assert(GV->getValueType() == ObjCTypes.ClassTy &&
"Forward metaclass reference has incorrect type.");
return GV;
}
llvm::Value *CGObjCMac::EmitSuperClassRef(const ObjCInterfaceDecl *ID) {
std::string Name = "OBJC_CLASS_" + ID->getNameAsString();
llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name, true);
if (!GV)
GV = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassTy, false,
llvm::GlobalValue::PrivateLinkage, nullptr,
Name);
assert(GV->getValueType() == ObjCTypes.ClassTy &&
"Forward class metadata reference has incorrect type.");
return GV;
}
Emit a "class extension", which in this specific context means extra
data that doesn't fit in the normal fragile-ABI class structure, and
has nothing to do with the language concept of a class extension.
struct objc_class_ext {
uint32_t size;
const char *weak_ivar_layout;
struct _objc_property_list *properties;
};
*/
llvm::Constant *
CGObjCMac::EmitClassExtension(const ObjCImplementationDecl *ID,
CharUnits InstanceSize, bool hasMRCWeakIvars,
bool isMetaclass) {
llvm::Constant *layout;
if (isMetaclass) {
layout = llvm::ConstantPointerNull::get(CGM.Int8PtrTy);
} else {
layout = BuildWeakIvarLayout(ID, CharUnits::Zero(), InstanceSize,
hasMRCWeakIvars);
}
llvm::Constant *propertyList =
EmitPropertyList((isMetaclass ? Twine("_OBJC_$_CLASS_PROP_LIST_")
: Twine("_OBJC_$_PROP_LIST_"))
+ ID->getName(),
ID, ID->getClassInterface(), ObjCTypes, isMetaclass);
if (layout->isNullValue() && propertyList->isNullValue()) {
return llvm::Constant::getNullValue(ObjCTypes.ClassExtensionPtrTy);
}
uint64_t size =
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ClassExtensionTy);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ClassExtensionTy);
values.addInt(ObjCTypes.IntTy, size);
values.add(layout);
values.add(propertyList);
return CreateMetadataVar("OBJC_CLASSEXT_" + ID->getName(), values,
"__OBJC,__class_ext,regular,no_dead_strip",
CGM.getPointerAlign(), 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) {
if (ForClass)
return llvm::Constant::getNullValue(ObjCTypes.IvarListPtrTy);
const ObjCInterfaceDecl *OID = ID->getClassInterface();
ConstantInitBuilder builder(CGM);
auto ivarList = builder.beginStruct();
auto countSlot = ivarList.addPlaceholder();
auto ivars = ivarList.beginArray(ObjCTypes.IvarTy);
for (const ObjCIvarDecl *IVD = OID->all_declared_ivar_begin();
IVD; IVD = IVD->getNextIvar()) {
if (!IVD->getDeclName())
continue;
auto ivar = ivars.beginStruct(ObjCTypes.IvarTy);
ivar.add(GetMethodVarName(IVD->getIdentifier()));
ivar.add(GetMethodVarType(IVD));
ivar.addInt(ObjCTypes.IntTy, ComputeIvarBaseOffset(CGM, OID, IVD));
ivar.finishAndAddTo(ivars);
}
auto count = ivars.size();
if (count == 0) {
ivars.abandon();
ivarList.abandon();
return llvm::Constant::getNullValue(ObjCTypes.IvarListPtrTy);
}
ivars.finishAndAddTo(ivarList);
ivarList.fillPlaceholderWithInt(countSlot, ObjCTypes.IntTy, count);
llvm::GlobalVariable *GV;
GV = CreateMetadataVar("OBJC_INSTANCE_VARIABLES_" + ID->getName(), ivarList,
"__OBJC,__instance_vars,regular,no_dead_strip",
CGM.getPointerAlign(), true);
return GV;
}
void CGObjCMac::emitMethodDescriptionConstant(ConstantArrayBuilder &builder,
const ObjCMethodDecl *MD) {
auto description = builder.beginStruct(ObjCTypes.MethodDescriptionTy);
description.add(GetMethodVarName(MD->getSelector()));
description.add(GetMethodVarType(MD));
description.finishAndAddTo(builder);
}
void CGObjCMac::emitMethodConstant(ConstantArrayBuilder &builder,
const ObjCMethodDecl *MD) {
llvm::Function *fn = GetMethodDefinition(MD);
assert(fn && "no definition registered for method");
auto method = builder.beginStruct(ObjCTypes.MethodTy);
method.add(GetMethodVarName(MD->getSelector()));
method.add(GetMethodVarType(MD));
method.add(fn);
method.finishAndAddTo(builder);
}
llvm::Constant *CGObjCMac::emitMethodList(Twine name, MethodListType MLT,
ArrayRef<const ObjCMethodDecl *> methods) {
StringRef prefix;
StringRef section;
bool forProtocol = false;
switch (MLT) {
case MethodListType::CategoryInstanceMethods:
prefix = "OBJC_CATEGORY_INSTANCE_METHODS_";
section = "__OBJC,__cat_inst_meth,regular,no_dead_strip";
forProtocol = false;
break;
case MethodListType::CategoryClassMethods:
prefix = "OBJC_CATEGORY_CLASS_METHODS_";
section = "__OBJC,__cat_cls_meth,regular,no_dead_strip";
forProtocol = false;
break;
case MethodListType::InstanceMethods:
prefix = "OBJC_INSTANCE_METHODS_";
section = "__OBJC,__inst_meth,regular,no_dead_strip";
forProtocol = false;
break;
case MethodListType::ClassMethods:
prefix = "OBJC_CLASS_METHODS_";
section = "__OBJC,__cls_meth,regular,no_dead_strip";
forProtocol = false;
break;
case MethodListType::ProtocolInstanceMethods:
prefix = "OBJC_PROTOCOL_INSTANCE_METHODS_";
section = "__OBJC,__cat_inst_meth,regular,no_dead_strip";
forProtocol = true;
break;
case MethodListType::ProtocolClassMethods:
prefix = "OBJC_PROTOCOL_CLASS_METHODS_";
section = "__OBJC,__cat_cls_meth,regular,no_dead_strip";
forProtocol = true;
break;
case MethodListType::OptionalProtocolInstanceMethods:
prefix = "OBJC_PROTOCOL_INSTANCE_METHODS_OPT_";
section = "__OBJC,__cat_inst_meth,regular,no_dead_strip";
forProtocol = true;
break;
case MethodListType::OptionalProtocolClassMethods:
prefix = "OBJC_PROTOCOL_CLASS_METHODS_OPT_";
section = "__OBJC,__cat_cls_meth,regular,no_dead_strip";
forProtocol = true;
break;
}
if (methods.empty())
return llvm::Constant::getNullValue(forProtocol
? ObjCTypes.MethodDescriptionListPtrTy
: ObjCTypes.MethodListPtrTy);
if (forProtocol) {
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
values.addInt(ObjCTypes.IntTy, methods.size());
auto methodArray = values.beginArray(ObjCTypes.MethodDescriptionTy);
for (auto MD : methods) {
emitMethodDescriptionConstant(methodArray, MD);
}
methodArray.finishAndAddTo(values);
llvm::GlobalVariable *GV = CreateMetadataVar(prefix + name, values, section,
CGM.getPointerAlign(), true);
return GV;
}
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
values.addNullPointer(ObjCTypes.Int8PtrTy);
values.addInt(ObjCTypes.IntTy, methods.size());
auto methodArray = values.beginArray(ObjCTypes.MethodTy);
for (auto MD : methods) {
if (!MD->isDirectMethod())
emitMethodConstant(methodArray, MD);
}
methodArray.finishAndAddTo(values);
llvm::GlobalVariable *GV = CreateMetadataVar(prefix + name, values, section,
CGM.getPointerAlign(), true);
return GV;
}
llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) {
llvm::Function *Method;
if (OMD->isDirectMethod()) {
Method = GenerateDirectMethod(OMD, CD);
} else {
auto Name = getSymbolNameForMethod(OMD);
CodeGenTypes &Types = CGM.getTypes();
llvm::FunctionType *MethodTy =
Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD));
Method =
llvm::Function::Create(MethodTy, llvm::GlobalValue::InternalLinkage,
Name, &CGM.getModule());
}
MethodDefinitions.insert(std::make_pair(OMD, Method));
return Method;
}
llvm::Function *
CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) {
auto *COMD = OMD->getCanonicalDecl();
auto I = DirectMethodDefinitions.find(COMD);
llvm::Function *OldFn = nullptr, *Fn = nullptr;
if (I != DirectMethodDefinitions.end()) {
if (!OMD->getBody() || COMD->getReturnType() == OMD->getReturnType())
return I->second;
OldFn = I->second;
}
CodeGenTypes &Types = CGM.getTypes();
llvm::FunctionType *MethodTy =
Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD));
if (OldFn) {
Fn = llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage,
"", &CGM.getModule());
Fn->takeName(OldFn);
OldFn->replaceAllUsesWith(Fn);
OldFn->eraseFromParent();
I->second = Fn;
} else {
auto Name = getSymbolNameForMethod(OMD, false);
Fn = llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage,
Name, &CGM.getModule());
DirectMethodDefinitions.insert(std::make_pair(COMD, Fn));
}
return Fn;
}
void CGObjCCommonMac::GenerateDirectMethodPrologue(
CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD,
const ObjCContainerDecl *CD) {
auto &Builder = CGF.Builder;
bool ReceiverCanBeNull = true;
auto selfAddr = CGF.GetAddrOfLocalVar(OMD->getSelfDecl());
auto selfValue = Builder.CreateLoad(selfAddr);
if (OMD->isClassMethod()) {
const ObjCInterfaceDecl *OID = cast<ObjCInterfaceDecl>(CD);
assert(OID &&
"GenerateDirectMethod() should be called with the Class Interface");
Selector SelfSel = GetNullarySelector("self", CGM.getContext());
auto ResultType = CGF.getContext().getObjCIdType();
RValue result;
CallArgList Args;
result = GeneratePossiblySpecializedMessageSend(
CGF, ReturnValueSlot(), ResultType, SelfSel, selfValue, Args, OID,
nullptr, true);
Builder.CreateStore(result.getScalarVal(), selfAddr);
ReceiverCanBeNull = isWeakLinkedClass(OID);
}
if (ReceiverCanBeNull) {
llvm::BasicBlock *SelfIsNilBlock =
CGF.createBasicBlock("objc_direct_method.self_is_nil");
llvm::BasicBlock *ContBlock =
CGF.createBasicBlock("objc_direct_method.cont");
auto selfTy = cast<llvm::PointerType>(selfValue->getType());
auto Zero = llvm::ConstantPointerNull::get(selfTy);
llvm::MDBuilder MDHelper(CGM.getLLVMContext());
Builder.CreateCondBr(Builder.CreateICmpEQ(selfValue, Zero), SelfIsNilBlock,
ContBlock, MDHelper.createUnlikelyBranchWeights());
CGF.EmitBlock(SelfIsNilBlock);
auto retTy = OMD->getReturnType();
Builder.SetInsertPoint(SelfIsNilBlock);
if (!retTy->isVoidType()) {
CGF.EmitNullInitialization(CGF.ReturnValue, retTy);
}
CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
CGF.EmitBlock(ContBlock);
Builder.SetInsertPoint(ContBlock);
}
if (OMD->getCmdDecl()->isUsed()) {
CGF.EmitVarDecl(*OMD->getCmdDecl());
Builder.CreateStore(GetSelector(CGF, OMD),
CGF.GetAddrOfLocalVar(OMD->getCmdDecl()));
}
}
llvm::GlobalVariable *CGObjCCommonMac::CreateMetadataVar(Twine Name,
ConstantStructBuilder &Init,
StringRef Section,
CharUnits Align,
bool AddToUsed) {
llvm::GlobalValue::LinkageTypes LT =
getLinkageTypeForObjCMetadata(CGM, Section);
llvm::GlobalVariable *GV =
Init.finishAndCreateGlobal(Name, Align, false, LT);
if (!Section.empty())
GV->setSection(Section);
if (AddToUsed)
CGM.addCompilerUsedGlobal(GV);
return GV;
}
llvm::GlobalVariable *CGObjCCommonMac::CreateMetadataVar(Twine Name,
llvm::Constant *Init,
StringRef Section,
CharUnits Align,
bool AddToUsed) {
llvm::Type *Ty = Init->getType();
llvm::GlobalValue::LinkageTypes LT =
getLinkageTypeForObjCMetadata(CGM, Section);
llvm::GlobalVariable *GV =
new llvm::GlobalVariable(CGM.getModule(), Ty, false, LT, Init, Name);
if (!Section.empty())
GV->setSection(Section);
GV->setAlignment(Align.getAsAlign());
if (AddToUsed)
CGM.addCompilerUsedGlobal(GV);
return GV;
}
llvm::GlobalVariable *
CGObjCCommonMac::CreateCStringLiteral(StringRef Name, ObjCLabelType Type,
bool ForceNonFragileABI,
bool NullTerminate) {
StringRef Label;
switch (Type) {
case ObjCLabelType::ClassName: Label = "OBJC_CLASS_NAME_"; break;
case ObjCLabelType::MethodVarName: Label = "OBJC_METH_VAR_NAME_"; break;
case ObjCLabelType::MethodVarType: Label = "OBJC_METH_VAR_TYPE_"; break;
case ObjCLabelType::PropertyName: Label = "OBJC_PROP_NAME_ATTR_"; break;
}
bool NonFragile = ForceNonFragileABI || isNonFragileABI();
StringRef Section;
switch (Type) {
case ObjCLabelType::ClassName:
Section = NonFragile ? "__TEXT,__objc_classname,cstring_literals"
: "__TEXT,__cstring,cstring_literals";
break;
case ObjCLabelType::MethodVarName:
Section = NonFragile ? "__TEXT,__objc_methname,cstring_literals"
: "__TEXT,__cstring,cstring_literals";
break;
case ObjCLabelType::MethodVarType:
Section = NonFragile ? "__TEXT,__objc_methtype,cstring_literals"
: "__TEXT,__cstring,cstring_literals";
break;
case ObjCLabelType::PropertyName:
Section = NonFragile ? "__TEXT,__objc_methname,cstring_literals"
: "__TEXT,__cstring,cstring_literals";
break;
}
llvm::Constant *Value =
llvm::ConstantDataArray::getString(VMContext, Name, NullTerminate);
llvm::GlobalVariable *GV =
new llvm::GlobalVariable(CGM.getModule(), Value->getType(),
true,
llvm::GlobalValue::PrivateLinkage, Value, Label);
if (CGM.getTriple().isOSBinFormatMachO())
GV->setSection(Section);
GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
GV->setAlignment(CharUnits::One().getAsAlign());
CGM.addCompilerUsedGlobal(GV);
return GV;
}
llvm::Function *CGObjCMac::ModuleInitFunction() {
FinishModule();
return nullptr;
}
llvm::FunctionCallee CGObjCMac::GetPropertyGetFunction() {
return ObjCTypes.getGetPropertyFn();
}
llvm::FunctionCallee CGObjCMac::GetPropertySetFunction() {
return ObjCTypes.getSetPropertyFn();
}
llvm::FunctionCallee CGObjCMac::GetOptimizedPropertySetFunction(bool atomic,
bool copy) {
return ObjCTypes.getOptimizedSetPropertyFn(atomic, copy);
}
llvm::FunctionCallee CGObjCMac::GetGetStructFunction() {
return ObjCTypes.getCopyStructFn();
}
llvm::FunctionCallee CGObjCMac::GetSetStructFunction() {
return ObjCTypes.getCopyStructFn();
}
llvm::FunctionCallee CGObjCMac::GetCppAtomicObjectGetFunction() {
return ObjCTypes.getCppAtomicObjectFunction();
}
llvm::FunctionCallee CGObjCMac::GetCppAtomicObjectSetFunction() {
return ObjCTypes.getCppAtomicObjectFunction();
}
llvm::FunctionCallee CGObjCMac::EnumerationMutationFunction() {
return ObjCTypes.getEnumerationMutationFn();
}
void CGObjCMac::EmitTryStmt(CodeGenFunction &CGF, const ObjCAtTryStmt &S) {
return EmitTryOrSynchronizedStmt(CGF, S);
}
void CGObjCMac::EmitSynchronizedStmt(CodeGenFunction &CGF,
const ObjCAtSynchronizedStmt &S) {
return EmitTryOrSynchronizedStmt(CGF, S);
}
namespace {
struct PerformFragileFinally final : EHScopeStack::Cleanup {
const Stmt &S;
Address SyncArgSlot;
Address CallTryExitVar;
Address ExceptionData;
ObjCTypesHelper &ObjCTypes;
PerformFragileFinally(const Stmt *S,
Address SyncArgSlot,
Address CallTryExitVar,
Address ExceptionData,
ObjCTypesHelper *ObjCTypes)
: S(*S), SyncArgSlot(SyncArgSlot), CallTryExitVar(CallTryExitVar),
ExceptionData(ExceptionData), ObjCTypes(*ObjCTypes) {}
void Emit(CodeGenFunction &CGF, Flags flags) override {
llvm::BasicBlock *FinallyCallExit =
CGF.createBasicBlock("finally.call_exit");
llvm::BasicBlock *FinallyNoCallExit =
CGF.createBasicBlock("finally.no_call_exit");
CGF.Builder.CreateCondBr(CGF.Builder.CreateLoad(CallTryExitVar),
FinallyCallExit, FinallyNoCallExit);
CGF.EmitBlock(FinallyCallExit);
CGF.EmitNounwindRuntimeCall(ObjCTypes.getExceptionTryExitFn(),
ExceptionData.emitRawPointer(CGF));
CGF.EmitBlock(FinallyNoCallExit);
if (isa<ObjCAtTryStmt>(S)) {
if (const ObjCAtFinallyStmt* FinallyStmt =
cast<ObjCAtTryStmt>(S).getFinallyStmt()) {
if (flags.isForEHCleanup()) return;
llvm::Value *CurCleanupDest =
CGF.Builder.CreateLoad(CGF.getNormalCleanupDestSlot());
CGF.EmitStmt(FinallyStmt->getFinallyBody());
if (CGF.HaveInsertPoint()) {
CGF.Builder.CreateStore(CurCleanupDest,
CGF.getNormalCleanupDestSlot());
} else {
CGF.EnsureInsertPoint();
}
}
} else {
llvm::Value *SyncArg = CGF.Builder.CreateLoad(SyncArgSlot);
CGF.EmitNounwindRuntimeCall(ObjCTypes.getSyncExitFn(), SyncArg);
}
}
};
class FragileHazards {
CodeGenFunction &CGF;
SmallVector<llvm::Value*, 20> Locals;
llvm::DenseSet<llvm::BasicBlock*> BlocksBeforeTry;
llvm::InlineAsm *ReadHazard;
llvm::InlineAsm *WriteHazard;
llvm::FunctionType *GetAsmFnType();
void collectLocals();
void emitReadHazard(CGBuilderTy &Builder);
public:
FragileHazards(CodeGenFunction &CGF);
void emitWriteHazard();
void emitHazardsInNewBlocks();
};
}
FragileHazards::FragileHazards(CodeGenFunction &CGF) : CGF(CGF) {
collectLocals();
if (Locals.empty()) return;
for (llvm::Function::iterator
I = CGF.CurFn->begin(), E = CGF.CurFn->end(); I != E; ++I)
BlocksBeforeTry.insert(&*I);
llvm::FunctionType *AsmFnTy = GetAsmFnType();
{
std::string Constraint;
for (unsigned I = 0, E = Locals.size(); I != E; ++I) {
if (I) Constraint += ',';
Constraint += "*m";
}
ReadHazard = llvm::InlineAsm::get(AsmFnTy, "", Constraint, true, false);
}
{
std::string Constraint;
for (unsigned I = 0, E = Locals.size(); I != E; ++I) {
if (I) Constraint += ',';
Constraint += "=*m";
}
WriteHazard = llvm::InlineAsm::get(AsmFnTy, "", Constraint, true, false);
}
}
void FragileHazards::emitWriteHazard() {
if (Locals.empty()) return;
llvm::CallInst *Call = CGF.EmitNounwindRuntimeCall(WriteHazard, Locals);
for (auto Pair : llvm::enumerate(Locals))
Call->addParamAttr(Pair.index(), llvm::Attribute::get(
CGF.getLLVMContext(), llvm::Attribute::ElementType,
cast<llvm::AllocaInst>(Pair.value())->getAllocatedType()));
}
void FragileHazards::emitReadHazard(CGBuilderTy &Builder) {
assert(!Locals.empty());
llvm::CallInst *call = Builder.CreateCall(ReadHazard, Locals);
call->setDoesNotThrow();
call->setCallingConv(CGF.getRuntimeCC());
for (auto Pair : llvm::enumerate(Locals))
call->addParamAttr(Pair.index(), llvm::Attribute::get(
Builder.getContext(), llvm::Attribute::ElementType,
cast<llvm::AllocaInst>(Pair.value())->getAllocatedType()));
}
void FragileHazards::emitHazardsInNewBlocks() {
if (Locals.empty()) return;
CGBuilderTy Builder(CGF, CGF.getLLVMContext());
for (llvm::Function::iterator
FI = CGF.CurFn->begin(), FE = CGF.CurFn->end(); FI != FE; ++FI) {
llvm::BasicBlock &BB = *FI;
if (BlocksBeforeTry.count(&BB)) continue;
for (llvm::BasicBlock::iterator
BI = BB.begin(), BE = BB.end(); BI != BE; ++BI) {
llvm::Instruction &I = *BI;
if (!isa<llvm::CallInst>(I) && !isa<llvm::InvokeInst>(I))
continue;
if (isa<llvm::IntrinsicInst>(I))
continue;
if (cast<llvm::CallBase>(I).doesNotThrow())
continue;
Builder.SetInsertPoint(&BB, BI);
emitReadHazard(Builder);
}
}
}
static void addIfPresent(llvm::DenseSet<llvm::Value*> &S, Address V) {
if (V.isValid())
if (llvm::Value *Ptr = V.getBasePointer())
S.insert(Ptr);
}
void FragileHazards::collectLocals() {
llvm::DenseSet<llvm::Value*> AllocasToIgnore;
addIfPresent(AllocasToIgnore, CGF.ReturnValue);
addIfPresent(AllocasToIgnore, CGF.NormalCleanupDest);
llvm::BasicBlock &Entry = CGF.CurFn->getEntryBlock();
for (llvm::BasicBlock::iterator
I = Entry.begin(), E = Entry.end(); I != E; ++I)
if (isa<llvm::AllocaInst>(*I) && !AllocasToIgnore.count(&*I))
Locals.push_back(&*I);
}
llvm::FunctionType *FragileHazards::GetAsmFnType() {
SmallVector<llvm::Type *, 16> tys(Locals.size());
for (unsigned i = 0, e = Locals.size(); i != e; ++i)
tys[i] = Locals[i]->getType();
return llvm::FunctionType::get(CGF.VoidTy, tys, false);
}
Objective-C setjmp-longjmp (sjlj) Exception Handling
--
A catch buffer is a setjmp buffer plus:
- a pointer to the exception that was caught
- a pointer to the previous exception data buffer
- two pointers of reserved storage
Therefore catch buffers form a stack, with a pointer to the top
of the stack kept in thread-local storage.
objc_exception_try_enter pushes a catch buffer onto the EH stack.
objc_exception_try_exit pops the given catch buffer, which is
required to be the top of the EH stack.
objc_exception_throw pops the top of the EH stack, writes the
thrown exception into the appropriate field, and longjmps
to the setjmp buffer. It crashes the process (with a printf
and an abort()) if there are no catch buffers on the stack.
objc_exception_extract just reads the exception pointer out of the
catch buffer.
There's no reason an implementation couldn't use a light-weight
setjmp here --- something like __builtin_setjmp, but API-compatible
with the heavyweight setjmp. This will be more important if we ever
want to implement correct ObjC/C++ exception interactions for the
fragile ABI.
Note that for this use of setjmp/longjmp to be correct in the presence of
optimization, we use inline assembly on the set of local variables to force
flushing locals to memory immediately before any protected calls and to
inhibit optimizing locals across the setjmp->catch edge.
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
--
'@throw;' is supported by pushing the currently-caught exception
onto ObjCEHStack while the @catch blocks are emitted.
Branches through the @finally block are handled with an ordinary
normal cleanup. We do not register an EH cleanup; fragile-ABI ObjC
exceptions are not compatible with C++ exceptions, and this is
hardly the only place where this will go wrong.
@synchronized(expr) { stmt; } is emitted as if it were:
id synch_value = expr;
objc_sync_enter(synch_value);
@try { stmt; } @finally { objc_sync_exit(synch_value); }
*/
void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
const Stmt &S) {
bool isTry = isa<ObjCAtTryStmt>(S);
CodeGenFunction::JumpDest FinallyEnd =
CGF.getJumpDestInCurrentScope("finally.end");
CodeGenFunction::JumpDest FinallyRethrow =
CGF.getJumpDestInCurrentScope("finally.rethrow");
Address SyncArgSlot = Address::invalid();
if (!isTry) {
llvm::Value *SyncArg =
CGF.EmitScalarExpr(cast<ObjCAtSynchronizedStmt>(S).getSynchExpr());
SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy);
CGF.EmitNounwindRuntimeCall(ObjCTypes.getSyncEnterFn(), SyncArg);
SyncArgSlot = CGF.CreateTempAlloca(SyncArg->getType(),
CGF.getPointerAlign(), "sync.arg");
CGF.Builder.CreateStore(SyncArg, SyncArgSlot);
}
Address ExceptionData = CGF.CreateTempAlloca(ObjCTypes.ExceptionDataTy,
CGF.getPointerAlign(),
"exceptiondata.ptr");
FragileHazards Hazards(CGF);
Address CallTryExitVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(),
CharUnits::One(),
"_call_try_exit");
Address PropagatingExnVar = Address::invalid();
CGF.EHStack.pushCleanup<PerformFragileFinally>(NormalAndEHCleanup, &S,
SyncArgSlot,
CallTryExitVar,
ExceptionData,
&ObjCTypes);
CGF.EmitNounwindRuntimeCall(ObjCTypes.getExceptionTryEnterFn(),
ExceptionData.emitRawPointer(CGF));
llvm::Constant *Zero = llvm::ConstantInt::get(CGF.Builder.getInt32Ty(), 0);
llvm::Value *GEPIndexes[] = { Zero, Zero, Zero };
llvm::Value *SetJmpBuffer = CGF.Builder.CreateGEP(
ObjCTypes.ExceptionDataTy, ExceptionData.emitRawPointer(CGF), GEPIndexes,
"setjmp_buffer");
llvm::CallInst *SetJmpResult = CGF.EmitNounwindRuntimeCall(
ObjCTypes.getSetJmpFn(), SetJmpBuffer, "setjmp_result");
SetJmpResult->setCanReturnTwice();
llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try");
llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler");
llvm::Value *DidCatch =
CGF.Builder.CreateIsNotNull(SetJmpResult, "did_catch_exception");
CGF.Builder.CreateCondBr(DidCatch, TryHandler, TryBlock);
CGF.EmitBlock(TryBlock);
CGF.Builder.CreateStore(CGF.Builder.getTrue(), CallTryExitVar);
CGF.EmitStmt(isTry ? cast<ObjCAtTryStmt>(S).getTryBody()
: cast<ObjCAtSynchronizedStmt>(S).getSynchBody());
CGBuilderTy::InsertPoint TryFallthroughIP = CGF.Builder.saveAndClearIP();
CGF.EmitBlock(TryHandler);
Hazards.emitWriteHazard();
if (!isTry || !cast<ObjCAtTryStmt>(S).getNumCatchStmts()) {
CGF.Builder.CreateStore(CGF.Builder.getFalse(), CallTryExitVar);
CGF.EmitBranchThroughCleanup(FinallyRethrow);
} else {
llvm::CallInst *Caught = CGF.EmitNounwindRuntimeCall(
ObjCTypes.getExceptionExtractFn(), ExceptionData.emitRawPointer(CGF),
"caught");
CGF.ObjCEHValueStack.push_back(Caught);
const ObjCAtTryStmt* AtTryStmt = cast<ObjCAtTryStmt>(&S);
bool HasFinally = (AtTryStmt->getFinallyStmt() != nullptr);
llvm::BasicBlock *CatchBlock = nullptr;
llvm::BasicBlock *CatchHandler = nullptr;
if (HasFinally) {
PropagatingExnVar = CGF.CreateTempAlloca(Caught->getType(),
CGF.getPointerAlign(),
"propagating_exception");
CGF.Builder.CreateStore(Caught, PropagatingExnVar);
CGF.EmitNounwindRuntimeCall(ObjCTypes.getExceptionTryEnterFn(),
ExceptionData.emitRawPointer(CGF));
llvm::CallInst *SetJmpResult =
CGF.EmitNounwindRuntimeCall(ObjCTypes.getSetJmpFn(),
SetJmpBuffer, "setjmp.result");
SetJmpResult->setCanReturnTwice();
llvm::Value *Threw =
CGF.Builder.CreateIsNotNull(SetJmpResult, "did_catch_exception");
CatchBlock = CGF.createBasicBlock("catch");
CatchHandler = CGF.createBasicBlock("catch_for_catch");
CGF.Builder.CreateCondBr(Threw, CatchHandler, CatchBlock);
CGF.EmitBlock(CatchBlock);
}
CGF.Builder.CreateStore(CGF.Builder.getInt1(HasFinally), CallTryExitVar);
bool AllMatched = false;
for (const ObjCAtCatchStmt *CatchStmt : AtTryStmt->catch_stmts()) {
const VarDecl *CatchParam = CatchStmt->getCatchParamDecl();
const ObjCObjectPointerType *OPT = nullptr;
if (!CatchParam) {
AllMatched = true;
} else {
OPT = CatchParam->getType()->getAs<ObjCObjectPointerType>();
if (OPT && (OPT->isObjCIdType() || OPT->isObjCQualifiedIdType()))
AllMatched = true;
}
if (AllMatched) {
CodeGenFunction::RunCleanupsScope CatchVarCleanups(CGF);
if (CatchParam) {
CGF.EmitAutoVarDecl(*CatchParam);
assert(CGF.HaveInsertPoint() && "DeclStmt destroyed insert point?");
EmitInitOfCatchParam(CGF, Caught, CatchParam);
}
CGF.EmitStmt(CatchStmt->getCatchBody());
CatchVarCleanups.ForceCleanup();
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!");
llvm::Value *Class = EmitClassRef(CGF, IDecl);
llvm::Value *matchArgs[] = { Class, Caught };
llvm::CallInst *Match =
CGF.EmitNounwindRuntimeCall(ObjCTypes.getExceptionMatchFn(),
matchArgs, "match");
llvm::BasicBlock *MatchedBlock = CGF.createBasicBlock("match");
llvm::BasicBlock *NextCatchBlock = CGF.createBasicBlock("catch.next");
CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNotNull(Match, "matched"),
MatchedBlock, NextCatchBlock);
CGF.EmitBlock(MatchedBlock);
CodeGenFunction::RunCleanupsScope CatchVarCleanups(CGF);
CGF.EmitAutoVarDecl(*CatchParam);
assert(CGF.HaveInsertPoint() && "DeclStmt destroyed insert point?");
llvm::Value *Tmp =
CGF.Builder.CreateBitCast(Caught,
CGF.ConvertType(CatchParam->getType()));
EmitInitOfCatchParam(CGF, Tmp, CatchParam);
CGF.EmitStmt(CatchStmt->getCatchBody());
CatchVarCleanups.ForceCleanup();
CGF.EmitBranchThroughCleanup(FinallyEnd);
CGF.EmitBlock(NextCatchBlock);
}
CGF.ObjCEHValueStack.pop_back();
if (Caught->use_empty())
Caught->eraseFromParent();
if (!AllMatched)
CGF.EmitBranchThroughCleanup(FinallyRethrow);
if (HasFinally) {
CGF.EmitBlock(CatchHandler);
assert(PropagatingExnVar.isValid());
llvm::CallInst *NewCaught = CGF.EmitNounwindRuntimeCall(
ObjCTypes.getExceptionExtractFn(), ExceptionData.emitRawPointer(CGF),
"caught");
CGF.Builder.CreateStore(NewCaught, PropagatingExnVar);
CGF.Builder.CreateStore(CGF.Builder.getFalse(), CallTryExitVar);
CGF.EmitBranchThroughCleanup(FinallyRethrow);
}
}
Hazards.emitHazardsInNewBlocks();
CGF.Builder.restoreIP(TryFallthroughIP);
if (CGF.HaveInsertPoint())
CGF.Builder.CreateStore(CGF.Builder.getTrue(), CallTryExitVar);
CGF.PopCleanupBlock();
CGF.EmitBlock(FinallyEnd.getBlock(), true);
CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP();
CGF.EmitBlock(FinallyRethrow.getBlock(), true);
if (CGF.HaveInsertPoint()) {
llvm::Value *PropagatingExn;
if (PropagatingExnVar.isValid()) {
PropagatingExn = CGF.Builder.CreateLoad(PropagatingExnVar);
} else {
llvm::CallInst *Caught = CGF.EmitNounwindRuntimeCall(
ObjCTypes.getExceptionExtractFn(), ExceptionData.emitRawPointer(CGF));
PropagatingExn = Caught;
}
CGF.EmitNounwindRuntimeCall(ObjCTypes.getExceptionThrowFn(),
PropagatingExn);
CGF.Builder.CreateUnreachable();
}
CGF.Builder.restoreIP(SavedIP);
}
void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtThrowStmt &S,
bool ClearInsertionPoint) {
llvm::Value *ExceptionAsObject;
if (const Expr *ThrowExpr = S.getThrowExpr()) {
llvm::Value *Exception = CGF.EmitObjCThrowOperand(ThrowExpr);
ExceptionAsObject =
CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy);
} else {
assert((!CGF.ObjCEHValueStack.empty() && CGF.ObjCEHValueStack.back()) &&
"Unexpected rethrow outside @catch block.");
ExceptionAsObject = CGF.ObjCEHValueStack.back();
}
CGF.EmitRuntimeCall(ObjCTypes.getExceptionThrowFn(), ExceptionAsObject)
->setDoesNotReturn();
CGF.Builder.CreateUnreachable();
if (ClearInsertionPoint)
CGF.Builder.ClearInsertionPoint();
}
llvm::Value * CGObjCMac::EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF,
Address AddrWeakObj) {
llvm::Type* DestTy = AddrWeakObj.getElementType();
llvm::Value *AddrWeakObjVal = CGF.Builder.CreateBitCast(
AddrWeakObj.emitRawPointer(CGF), ObjCTypes.PtrObjectPtrTy);
llvm::Value *read_weak =
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcReadWeakFn(),
AddrWeakObjVal, "weakread");
read_weak = CGF.Builder.CreateBitCast(read_weak, DestTy);
return read_weak;
}
void CGObjCMac::EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().getTypeAllocSize(SrcTy);
assert(Size <= 8 && "does not support size > 8");
src = (Size == 4) ? CGF.Builder.CreateBitCast(src, CGM.Int32Ty)
: CGF.Builder.CreateBitCast(src, CGM.Int64Ty);
src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy);
}
src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = { src, dstVal };
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignWeakFn(),
args, "weakassign");
}
void CGObjCMac::EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst,
bool threadlocal) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().getTypeAllocSize(SrcTy);
assert(Size <= 8 && "does not support size > 8");
src = (Size == 4) ? CGF.Builder.CreateBitCast(src, CGM.Int32Ty)
: CGF.Builder.CreateBitCast(src, CGM.Int64Ty);
src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy);
}
src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal};
if (!threadlocal)
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignGlobalFn(),
args, "globalassign");
else
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignThreadLocalFn(),
args, "threadlocalassign");
}
void CGObjCMac::EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst,
llvm::Value *ivarOffset) {
assert(ivarOffset && "EmitObjCIvarAssign - ivarOffset is NULL");
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().getTypeAllocSize(SrcTy);
assert(Size <= 8 && "does not support size > 8");
src = (Size == 4) ? CGF.Builder.CreateBitCast(src, CGM.Int32Ty)
: CGF.Builder.CreateBitCast(src, CGM.Int64Ty);
src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy);
}
src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal, ivarOffset};
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignIvarFn(), args);
}
void CGObjCMac::EmitObjCStrongCastAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().getTypeAllocSize(SrcTy);
assert(Size <= 8 && "does not support size > 8");
src = (Size == 4) ? CGF.Builder.CreateBitCast(src, CGM.Int32Ty)
: CGF.Builder.CreateBitCast(src, CGM.Int64Ty);
src = CGF.Builder.CreateIntToPtr(src, ObjCTypes.Int8PtrTy);
}
src = CGF.Builder.CreateBitCast(src, ObjCTypes.ObjectPtrTy);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal};
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignStrongCastFn(),
args, "strongassign");
}
void CGObjCMac::EmitGCMemmoveCollectable(CodeGen::CodeGenFunction &CGF,
Address DestPtr, Address SrcPtr,
llvm::Value *size) {
llvm::Value *args[] = {DestPtr.emitRawPointer(CGF),
SrcPtr.emitRawPointer(CGF), size};
CGF.EmitNounwindRuntimeCall(ObjCTypes.GcMemmoveCollectableFn(), args);
}
LValue CGObjCMac::EmitObjCValueForIvar(CodeGen::CodeGenFunction &CGF,
QualType ObjectTy,
llvm::Value *BaseValue,
const ObjCIvarDecl *Ivar,
unsigned CVRQualifiers) {
const ObjCInterfaceDecl *ID =
ObjectTy->castAs<ObjCObjectType>()->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);
}
std::string CGObjCCommonMac::GetSectionName(StringRef Section,
StringRef MachOAttributes) {
switch (CGM.getTriple().getObjectFormat()) {
case llvm::Triple::UnknownObjectFormat:
llvm_unreachable("unexpected object file format");
case llvm::Triple::MachO: {
if (MachOAttributes.empty())
return ("__DATA," + Section).str();
return ("__DATA," + Section + "," + MachOAttributes).str();
}
case llvm::Triple::ELF:
assert(Section.starts_with("__") && "expected the name to begin with __");
return Section.substr(2).str();
case llvm::Triple::COFF:
assert(Section.starts_with("__") && "expected the name to begin with __");
return ("." + Section.substr(2) + "$B").str();
case llvm::Triple::Wasm:
case llvm::Triple::GOFF:
case llvm::Triple::SPIRV:
case llvm::Triple::XCOFF:
case llvm::Triple::DXContainer:
llvm::report_fatal_error(
"Objective-C support is unimplemented for object file format");
}
llvm_unreachable("Unhandled llvm::Triple::ObjectFormatType enum");
}
enum ImageInfoFlags {
eImageInfo_FixAndContinue = (1 << 0),
eImageInfo_GarbageCollected = (1 << 1),
eImageInfo_GCOnly = (1 << 2),
eImageInfo_OptimizedByDyld = (1 << 3),
eImageInfo_CorrectedSynthesize = (1 << 4),
eImageInfo_ImageIsSimulated = (1 << 5),
eImageInfo_ClassProperties = (1 << 6)
};
void CGObjCCommonMac::EmitImageInfo() {
unsigned version = 0;
std::string Section =
(ObjCABI == 1)
? "__OBJC,__image_info,regular"
: GetSectionName("__objc_imageinfo", "regular,no_dead_strip");
llvm::Module &Mod = CGM.getModule();
Mod.addModuleFlag(llvm::Module::Error, "Objective-C Version", ObjCABI);
Mod.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version",
version);
Mod.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section",
llvm::MDString::get(VMContext, Section));
auto Int8Ty = llvm::Type::getInt8Ty(VMContext);
if (CGM.getLangOpts().getGC() == LangOptions::NonGC) {
Mod.addModuleFlag(llvm::Module::Error,
"Objective-C Garbage Collection",
llvm::ConstantInt::get(Int8Ty,0));
} else {
Mod.addModuleFlag(llvm::Module::Error,
"Objective-C Garbage Collection",
llvm::ConstantInt::get(Int8Ty,
(uint8_t)eImageInfo_GarbageCollected));
if (CGM.getLangOpts().getGC() == LangOptions::GCOnly) {
Mod.addModuleFlag(llvm::Module::Error, "Objective-C GC Only",
eImageInfo_GCOnly);
llvm::Metadata *Ops[2] = {
llvm::MDString::get(VMContext, "Objective-C Garbage Collection"),
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
Int8Ty, eImageInfo_GarbageCollected))};
Mod.addModuleFlag(llvm::Module::Require, "Objective-C GC Only",
llvm::MDNode::get(VMContext, Ops));
}
}
if (CGM.getTarget().getTriple().isSimulatorEnvironment())
Mod.addModuleFlag(llvm::Module::Error, "Objective-C Is Simulated",
eImageInfo_ImageIsSimulated);
Mod.addModuleFlag(llvm::Module::Error, "Objective-C Class Properties",
eImageInfo_ClassProperties);
}
static const int ModuleVersion = 7;
void CGObjCMac::EmitModuleInfo() {
uint64_t Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ModuleTy);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ModuleTy);
values.addInt(ObjCTypes.LongTy, ModuleVersion);
values.addInt(ObjCTypes.LongTy, Size);
values.add(GetClassName(StringRef("")));
values.add(EmitModuleSymbols());
CreateMetadataVar("OBJC_MODULES", values,
"__OBJC,__module_info,regular,no_dead_strip",
CGM.getPointerAlign(), true);
}
llvm::Constant *CGObjCMac::EmitModuleSymbols() {
unsigned NumClasses = DefinedClasses.size();
unsigned NumCategories = DefinedCategories.size();
if (!NumClasses && !NumCategories)
return llvm::Constant::getNullValue(ObjCTypes.SymtabPtrTy);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
values.addInt(ObjCTypes.LongTy, 0);
values.addNullPointer(ObjCTypes.SelectorPtrTy);
values.addInt(ObjCTypes.ShortTy, NumClasses);
values.addInt(ObjCTypes.ShortTy, NumCategories);
auto array = values.beginArray(ObjCTypes.Int8PtrTy);
for (unsigned i=0; i<NumClasses; i++) {
const ObjCInterfaceDecl *ID = ImplementedClasses[i];
assert(ID);
if (ObjCImplementationDecl *IMP = ID->getImplementation())
if (ID->isWeakImported() && !IMP->isWeakImported())
DefinedClasses[i]->setLinkage(llvm::GlobalVariable::ExternalLinkage);
array.add(DefinedClasses[i]);
}
for (unsigned i=0; i<NumCategories; i++)
array.add(DefinedCategories[i]);
array.finishAndAddTo(values);
llvm::GlobalVariable *GV = CreateMetadataVar(
"OBJC_SYMBOLS", values, "__OBJC,__symbols,regular,no_dead_strip",
CGM.getPointerAlign(), true);
return GV;
}
llvm::Value *CGObjCMac::EmitClassRefFromId(CodeGenFunction &CGF,
IdentifierInfo *II) {
LazySymbols.insert(II);
llvm::GlobalVariable *&Entry = ClassReferences[II];
if (!Entry) {
Entry =
CreateMetadataVar("OBJC_CLASS_REFERENCES_", GetClassName(II->getName()),
"__OBJC,__cls_refs,literal_pointers,no_dead_strip",
CGM.getPointerAlign(), true);
}
return CGF.Builder.CreateAlignedLoad(Entry->getValueType(), Entry,
CGF.getPointerAlign());
}
llvm::Value *CGObjCMac::EmitClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) {
if (ID->hasAttr<ObjCRuntimeVisibleAttr>())
return EmitClassRefViaRuntime(CGF, ID, ObjCTypes);
IdentifierInfo *RuntimeName =
&CGM.getContext().Idents.get(ID->getObjCRuntimeNameAsString());
return EmitClassRefFromId(CGF, RuntimeName);
}
llvm::Value *CGObjCMac::EmitNSAutoreleasePoolClassRef(CodeGenFunction &CGF) {
IdentifierInfo *II = &CGM.getContext().Idents.get("NSAutoreleasePool");
return EmitClassRefFromId(CGF, II);
}
llvm::Value *CGObjCMac::EmitSelector(CodeGenFunction &CGF, Selector Sel) {
return CGF.Builder.CreateLoad(EmitSelectorAddr(Sel));
}
ConstantAddress CGObjCMac::EmitSelectorAddr(Selector Sel) {
CharUnits Align = CGM.getPointerAlign();
llvm::GlobalVariable *&Entry = SelectorReferences[Sel];
if (!Entry) {
Entry = CreateMetadataVar(
"OBJC_SELECTOR_REFERENCES_", GetMethodVarName(Sel),
"__OBJC,__message_refs,literal_pointers,no_dead_strip", Align, true);
Entry->setExternallyInitialized(true);
}
return ConstantAddress(Entry, ObjCTypes.SelectorPtrTy, Align);
}
llvm::Constant *CGObjCCommonMac::GetClassName(StringRef RuntimeName) {
llvm::GlobalVariable *&Entry = ClassNames[RuntimeName];
if (!Entry)
Entry = CreateCStringLiteral(RuntimeName, ObjCLabelType::ClassName);
return getConstantGEP(VMContext, Entry, 0, 0);
}
llvm::Function *CGObjCCommonMac::GetMethodDefinition(const ObjCMethodDecl *MD) {
return MethodDefinitions.lookup(MD);
}
llvm::Constant *CGObjCCommonMac::GetIvarLayoutName(IdentifierInfo *Ident,
const ObjCCommonTypesHelper &ObjCTypes) {
return llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy);
}
void IvarLayoutBuilder::visitRecord(const RecordType *RT,
CharUnits offset) {
const RecordDecl *RD = RT->getDecl();
if (RD->isUnion())
IsDisordered = true;
const ASTRecordLayout *recLayout = nullptr;
visitAggregate(RD->field_begin(), RD->field_end(), offset,
[&](const FieldDecl *field) -> CharUnits {
if (!recLayout)
recLayout = &CGM.getContext().getASTRecordLayout(RD);
auto offsetInBits = recLayout->getFieldOffset(field->getFieldIndex());
return CGM.getContext().toCharUnitsFromBits(offsetInBits);
});
}
template <class Iterator, class GetOffsetFn>
void IvarLayoutBuilder::visitAggregate(Iterator begin, Iterator end,
CharUnits aggregateOffset,
const GetOffsetFn &getOffset) {
for (; begin != end; ++begin) {
auto field = *begin;
if (field->isBitField()) {
continue;
}
CharUnits fieldOffset = aggregateOffset + getOffset(field);
visitField(field, fieldOffset);
}
}
void IvarLayoutBuilder::visitField(const FieldDecl *field,
CharUnits fieldOffset) {
QualType fieldType = field->getType();
uint64_t numElts = 1;
if (auto arrayType = CGM.getContext().getAsIncompleteArrayType(fieldType)) {
numElts = 0;
fieldType = arrayType->getElementType();
}
while (auto arrayType = CGM.getContext().getAsConstantArrayType(fieldType)) {
numElts *= arrayType->getZExtSize();
fieldType = arrayType->getElementType();
}
assert(!fieldType->isArrayType() && "ivar of non-constant array type?");
if (numElts == 0) return;
if (auto recType = fieldType->getAs<RecordType>()) {
size_t oldEnd = IvarsInfo.size();
visitRecord(recType, fieldOffset);
auto numEltEntries = IvarsInfo.size() - oldEnd;
if (numElts != 1 && numEltEntries != 0) {
CharUnits eltSize = CGM.getContext().getTypeSizeInChars(recType);
for (uint64_t eltIndex = 1; eltIndex != numElts; ++eltIndex) {
for (size_t i = 0; i != numEltEntries; ++i) {
auto firstEntry = IvarsInfo[oldEnd + i];
IvarsInfo.push_back(IvarInfo(firstEntry.Offset + eltIndex * eltSize,
firstEntry.SizeInWords));
}
}
}
return;
}
Qualifiers::GC GCAttr = GetGCAttrTypeForType(CGM.getContext(), fieldType);
if ((ForStrongLayout && GCAttr == Qualifiers::Strong)
|| (!ForStrongLayout && GCAttr == Qualifiers::Weak)) {
assert(CGM.getContext().getTypeSizeInChars(fieldType)
== CGM.getPointerSize());
IvarsInfo.push_back(IvarInfo(fieldOffset, numElts));
}
}
llvm::Constant *IvarLayoutBuilder::buildBitmap(CGObjCCommonMac &CGObjC,
llvm::SmallVectorImpl<unsigned char> &buffer) {
const unsigned char MaxNibble = 0xF;
const unsigned char SkipMask = 0xF0, SkipShift = 4;
const unsigned char ScanMask = 0x0F, ScanShift = 0;
assert(!IvarsInfo.empty() && "generating bitmap for no data");
if (IsDisordered) {
llvm::array_pod_sort(IvarsInfo.begin(), IvarsInfo.end());
} else {
assert(llvm::is_sorted(IvarsInfo));
}
assert(IvarsInfo.back().Offset < InstanceEnd);
assert(buffer.empty());
auto skip = [&](unsigned numWords) {
assert(numWords > 0);
if (!buffer.empty() && !(buffer.back() & ScanMask)) {
unsigned lastSkip = buffer.back() >> SkipShift;
if (lastSkip < MaxNibble) {
unsigned claimed = std::min(MaxNibble - lastSkip, numWords);
numWords -= claimed;
lastSkip += claimed;
buffer.back() = (lastSkip << SkipShift);
}
}
while (numWords >= MaxNibble) {
buffer.push_back(MaxNibble << SkipShift);
numWords -= MaxNibble;
}
if (numWords) {
buffer.push_back(numWords << SkipShift);
}
};
auto scan = [&](unsigned numWords) {
assert(numWords > 0);
if (!buffer.empty()) {
unsigned lastScan = (buffer.back() & ScanMask) >> ScanShift;
if (lastScan < MaxNibble) {
unsigned claimed = std::min(MaxNibble - lastScan, numWords);
numWords -= claimed;
lastScan += claimed;
buffer.back() = (buffer.back() & SkipMask) | (lastScan << ScanShift);
}
}
while (numWords >= MaxNibble) {
buffer.push_back(MaxNibble << ScanShift);
numWords -= MaxNibble;
}
if (numWords) {
buffer.push_back(numWords << ScanShift);
}
};
unsigned endOfLastScanInWords = 0;
const CharUnits WordSize = CGM.getPointerSize();
for (auto &request : IvarsInfo) {
CharUnits beginOfScan = request.Offset - InstanceBegin;
if ((beginOfScan % WordSize) != 0) continue;
if (beginOfScan.isNegative()) {
assert(request.Offset + request.SizeInWords * WordSize <= InstanceBegin);
continue;
}
unsigned beginOfScanInWords = beginOfScan / WordSize;
unsigned endOfScanInWords = beginOfScanInWords + request.SizeInWords;
if (beginOfScanInWords > endOfLastScanInWords) {
skip(beginOfScanInWords - endOfLastScanInWords);
} else {
beginOfScanInWords = endOfLastScanInWords;
if (beginOfScanInWords >= endOfScanInWords) continue;
}
assert(beginOfScanInWords < endOfScanInWords);
scan(endOfScanInWords - beginOfScanInWords);
endOfLastScanInWords = endOfScanInWords;
}
if (buffer.empty())
return llvm::ConstantPointerNull::get(CGM.Int8PtrTy);
if (CGM.getLangOpts().getGC() != LangOptions::NonGC) {
unsigned lastOffsetInWords =
(InstanceEnd - InstanceBegin + WordSize - CharUnits::One()) / WordSize;
if (lastOffsetInWords > endOfLastScanInWords) {
skip(lastOffsetInWords - endOfLastScanInWords);
}
}
buffer.push_back(0);
auto *Entry = CGObjC.CreateCStringLiteral(
reinterpret_cast<char *>(buffer.data()), ObjCLabelType::ClassName);
return getConstantGEP(CGM.getLLVMContext(), Entry, 0, 0);
}
llvm::Constant *
CGObjCCommonMac::BuildIvarLayout(const ObjCImplementationDecl *OMD,
CharUnits beginOffset, CharUnits endOffset,
bool ForStrongLayout, bool HasMRCWeakIvars) {
llvm::Type *PtrTy = CGM.Int8PtrTy;
if (CGM.getLangOpts().getGC() == LangOptions::NonGC &&
!CGM.getLangOpts().ObjCAutoRefCount &&
(ForStrongLayout || !HasMRCWeakIvars))
return llvm::Constant::getNullValue(PtrTy);
const ObjCInterfaceDecl *OI = OMD->getClassInterface();
SmallVector<const ObjCIvarDecl*, 32> ivars;
CharUnits baseOffset;
if (CGM.getLangOpts().getGC() == LangOptions::NonGC) {
for (const ObjCIvarDecl *IVD = OI->all_declared_ivar_begin();
IVD; IVD = IVD->getNextIvar())
ivars.push_back(IVD);
if (isNonFragileABI()) {
baseOffset = beginOffset;
} else if (!ivars.empty()) {
baseOffset =
CharUnits::fromQuantity(ComputeIvarBaseOffset(CGM, OMD, ivars[0]));
} else {
baseOffset = CharUnits::Zero();
}
baseOffset = baseOffset.alignTo(CGM.getPointerAlign());
}
else {
CGM.getContext().DeepCollectObjCIvars(OI, true, ivars);
baseOffset = CharUnits::Zero();
}
if (ivars.empty())
return llvm::Constant::getNullValue(PtrTy);
IvarLayoutBuilder builder(CGM, baseOffset, endOffset, ForStrongLayout);
builder.visitAggregate(ivars.begin(), ivars.end(), CharUnits::Zero(),
[&](const ObjCIvarDecl *ivar) -> CharUnits {
return CharUnits::fromQuantity(ComputeIvarBaseOffset(CGM, OMD, ivar));
});
if (!builder.hasBitmapData())
return llvm::Constant::getNullValue(PtrTy);
llvm::SmallVector<unsigned char, 4> buffer;
llvm::Constant *C = builder.buildBitmap(*this, buffer);
if (CGM.getLangOpts().ObjCGCBitmapPrint && !buffer.empty()) {
printf("\n%s ivar layout for class '%s': ",
ForStrongLayout ? "strong" : "weak",
OMD->getClassInterface()->getName().str().c_str());
builder.dump(buffer);
}
return C;
}
llvm::Constant *CGObjCCommonMac::GetMethodVarName(Selector Sel) {
llvm::GlobalVariable *&Entry = MethodVarNames[Sel];
if (!Entry)
Entry = CreateCStringLiteral(Sel.getAsString(), ObjCLabelType::MethodVarName);
return getConstantGEP(VMContext, Entry, 0, 0);
}
llvm::Constant *CGObjCCommonMac::GetMethodVarName(IdentifierInfo *ID) {
return GetMethodVarName(CGM.getContext().Selectors.getNullarySelector(ID));
}
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 = CreateCStringLiteral(TypeStr, ObjCLabelType::MethodVarType);
return getConstantGEP(VMContext, Entry, 0, 0);
}
llvm::Constant *CGObjCCommonMac::GetMethodVarType(const ObjCMethodDecl *D,
bool Extended) {
std::string TypeStr =
CGM.getContext().getObjCEncodingForMethodDecl(D, Extended);
llvm::GlobalVariable *&Entry = MethodVarTypes[TypeStr];
if (!Entry)
Entry = CreateCStringLiteral(TypeStr, ObjCLabelType::MethodVarType);
return getConstantGEP(VMContext, Entry, 0, 0);
}
llvm::Constant *CGObjCCommonMac::GetPropertyName(IdentifierInfo *Ident) {
llvm::GlobalVariable *&Entry = PropertyNames[Ident];
if (!Entry)
Entry = CreateCStringLiteral(Ident->getName(), ObjCLabelType::PropertyName);
return getConstantGEP(VMContext, Entry, 0, 0);
}
llvm::Constant *
CGObjCCommonMac::GetPropertyTypeString(const ObjCPropertyDecl *PD,
const Decl *Container) {
std::string TypeStr =
CGM.getContext().getObjCEncodingForPropertyDecl(PD, Container);
return GetPropertyName(&CGM.getContext().Idents.get(TypeStr));
}
void CGObjCMac::FinishModule() {
EmitModuleInfo();
for (auto &entry : Protocols) {
llvm::GlobalVariable *global = entry.second;
if (global->hasInitializer())
continue;
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ProtocolTy);
values.addNullPointer(ObjCTypes.ProtocolExtensionPtrTy);
values.add(GetClassName(entry.first->getName()));
values.addNullPointer(ObjCTypes.ProtocolListPtrTy);
values.addNullPointer(ObjCTypes.MethodDescriptionListPtrTy);
values.addNullPointer(ObjCTypes.MethodDescriptionListPtrTy);
values.finishAndSetAsInitializer(global);
CGM.addCompilerUsedGlobal(global);
}
if ((!LazySymbols.empty() || !DefinedSymbols.empty()) &&
CGM.getTriple().isOSBinFormatMachO()) {
SmallString<256> Asm;
Asm += CGM.getModule().getModuleInlineAsm();
if (!Asm.empty() && Asm.back() != '\n')
Asm += '\n';
llvm::raw_svector_ostream OS(Asm);
for (const auto *Sym : DefinedSymbols)
OS << "\t.objc_class_name_" << Sym->getName() << "=0\n"
<< "\t.globl .objc_class_name_" << Sym->getName() << "\n";
for (const auto *Sym : LazySymbols)
OS << "\t.lazy_reference .objc_class_name_" << Sym->getName() << "\n";
for (const auto &Category : DefinedCategoryNames)
OS << "\t.objc_category_name_" << Category << "=0\n"
<< "\t.globl .objc_category_name_" << Category << "\n";
CGM.getModule().setModuleInlineAsm(OS.str());
}
}
CGObjCNonFragileABIMac::CGObjCNonFragileABIMac(CodeGen::CodeGenModule &cgm)
: CGObjCCommonMac(cgm), ObjCTypes(cgm), ObjCEmptyCacheVar(nullptr),
ObjCEmptyVtableVar(nullptr) {
ObjCABI = 2;
}
ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm)
: VMContext(cgm.getLLVMContext()), CGM(cgm), ExternalProtocolPtrTy(nullptr)
{
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
unsigned ProgramAS = CGM.getDataLayout().getProgramAddressSpace();
ShortTy = cast<llvm::IntegerType>(Types.ConvertType(Ctx.ShortTy));
IntTy = CGM.IntTy;
LongTy = cast<llvm::IntegerType>(Types.ConvertType(Ctx.LongTy));
Int8PtrTy = CGM.Int8PtrTy;
Int8PtrProgramASTy = llvm::PointerType::get(CGM.Int8Ty, ProgramAS);
Int8PtrPtrTy = CGM.Int8PtrPtrTy;
if (CGM.getTarget().getTriple().getArch() == llvm::Triple::aarch64)
IvarOffsetVarTy = IntTy;
else
IvarOffsetVarTy = LongTy;
ObjectPtrTy =
cast<llvm::PointerType>(Types.ConvertType(Ctx.getObjCIdType()));
PtrObjectPtrTy =
llvm::PointerType::getUnqual(ObjectPtrTy);
SelectorPtrTy =
cast<llvm::PointerType>(Types.ConvertType(Ctx.getObjCSelType()));
RecordDecl *RD = RecordDecl::Create(
Ctx, TagTypeKind::Struct, Ctx.getTranslationUnitDecl(), SourceLocation(),
SourceLocation(), &Ctx.Idents.get("_objc_super"));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(),
nullptr, Ctx.getObjCIdType(), nullptr, nullptr,
false, ICIS_NoInit));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(),
nullptr, Ctx.getObjCClassType(), nullptr,
nullptr, false, ICIS_NoInit));
RD->completeDefinition();
SuperCTy = Ctx.getTagDeclType(RD);
SuperPtrCTy = Ctx.getPointerType(SuperCTy);
SuperTy = cast<llvm::StructType>(Types.ConvertType(SuperCTy));
SuperPtrTy = llvm::PointerType::getUnqual(SuperTy);
PropertyTy = llvm::StructType::create("struct._prop_t", Int8PtrTy, Int8PtrTy);
PropertyListTy = llvm::StructType::create(
"struct._prop_list_t", IntTy, IntTy, llvm::ArrayType::get(PropertyTy, 0));
PropertyListPtrTy = llvm::PointerType::getUnqual(PropertyListTy);
MethodTy = llvm::StructType::create("struct._objc_method", SelectorPtrTy,
Int8PtrTy, Int8PtrProgramASTy);
CacheTy = llvm::StructType::create(VMContext, "struct._objc_cache");
CachePtrTy = llvm::PointerType::getUnqual(CacheTy);
}
ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
: ObjCCommonTypesHelper(cgm) {
MethodDescriptionTy = llvm::StructType::create(
"struct._objc_method_description", SelectorPtrTy, Int8PtrTy);
MethodDescriptionListTy =
llvm::StructType::create("struct._objc_method_description_list", IntTy,
llvm::ArrayType::get(MethodDescriptionTy, 0));
MethodDescriptionListPtrTy =
llvm::PointerType::getUnqual(MethodDescriptionListTy);
ProtocolExtensionTy = llvm::StructType::create(
"struct._objc_protocol_extension", IntTy, MethodDescriptionListPtrTy,
MethodDescriptionListPtrTy, PropertyListPtrTy, Int8PtrPtrTy,
PropertyListPtrTy);
ProtocolExtensionPtrTy = llvm::PointerType::getUnqual(ProtocolExtensionTy);
ProtocolTy =
llvm::StructType::create(VMContext, "struct._objc_protocol");
ProtocolListTy =
llvm::StructType::create(VMContext, "struct._objc_protocol_list");
ProtocolListTy->setBody(llvm::PointerType::getUnqual(ProtocolListTy), LongTy,
llvm::ArrayType::get(ProtocolTy, 0));
ProtocolTy->setBody(ProtocolExtensionPtrTy, Int8PtrTy,
llvm::PointerType::getUnqual(ProtocolListTy),
MethodDescriptionListPtrTy, MethodDescriptionListPtrTy);
ProtocolListPtrTy = llvm::PointerType::getUnqual(ProtocolListTy);
ProtocolPtrTy = llvm::PointerType::getUnqual(ProtocolTy);
IvarTy = llvm::StructType::create("struct._objc_ivar", Int8PtrTy, Int8PtrTy,
IntTy);
IvarListTy =
llvm::StructType::create(VMContext, "struct._objc_ivar_list");
IvarListPtrTy = llvm::PointerType::getUnqual(IvarListTy);
MethodListTy =
llvm::StructType::create(VMContext, "struct._objc_method_list");
MethodListPtrTy = llvm::PointerType::getUnqual(MethodListTy);
ClassExtensionTy = llvm::StructType::create(
"struct._objc_class_extension", IntTy, Int8PtrTy, PropertyListPtrTy);
ClassExtensionPtrTy = llvm::PointerType::getUnqual(ClassExtensionTy);
ClassTy = llvm::StructType::create(VMContext, "struct._objc_class");
ClassTy->setBody(llvm::PointerType::getUnqual(ClassTy),
llvm::PointerType::getUnqual(ClassTy), Int8PtrTy, LongTy,
LongTy, LongTy, IvarListPtrTy, MethodListPtrTy, CachePtrTy,
ProtocolListPtrTy, Int8PtrTy, ClassExtensionPtrTy);
ClassPtrTy = llvm::PointerType::getUnqual(ClassTy);
CategoryTy = llvm::StructType::create(
"struct._objc_category", Int8PtrTy, Int8PtrTy, MethodListPtrTy,
MethodListPtrTy, ProtocolListPtrTy, IntTy, PropertyListPtrTy,
PropertyListPtrTy);
SymtabTy = llvm::StructType::create("struct._objc_symtab", LongTy,
SelectorPtrTy, ShortTy, ShortTy,
llvm::ArrayType::get(Int8PtrTy, 0));
SymtabPtrTy = llvm::PointerType::getUnqual(SymtabTy);
ModuleTy = llvm::StructType::create("struct._objc_module", LongTy, LongTy,
Int8PtrTy, SymtabPtrTy);
uint64_t SetJmpBufferSize = 18;
llvm::Type *StackPtrTy = llvm::ArrayType::get(CGM.Int8PtrTy, 4);
ExceptionDataTy = llvm::StructType::create(
"struct._objc_exception_data",
llvm::ArrayType::get(CGM.Int32Ty, SetJmpBufferSize), StackPtrTy);
}
ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModule &cgm)
: ObjCCommonTypesHelper(cgm) {
MethodListnfABITy =
llvm::StructType::create("struct.__method_list_t", IntTy, IntTy,
llvm::ArrayType::get(MethodTy, 0));
MethodListnfABIPtrTy = llvm::PointerType::getUnqual(MethodListnfABITy);
ProtocolListnfABITy =
llvm::StructType::create(VMContext, "struct._objc_protocol_list");
ProtocolnfABITy = llvm::StructType::create(
"struct._protocol_t", ObjectPtrTy, Int8PtrTy,
llvm::PointerType::getUnqual(ProtocolListnfABITy), MethodListnfABIPtrTy,
MethodListnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy,
PropertyListPtrTy, IntTy, IntTy, Int8PtrPtrTy, Int8PtrTy,
PropertyListPtrTy);
ProtocolnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolnfABITy);
ProtocolListnfABITy->setBody(LongTy,
llvm::ArrayType::get(ProtocolnfABIPtrTy, 0));
ProtocolListnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolListnfABITy);
IvarnfABITy = llvm::StructType::create(
"struct._ivar_t", llvm::PointerType::getUnqual(IvarOffsetVarTy),
Int8PtrTy, Int8PtrTy, IntTy, IntTy);
IvarListnfABITy =
llvm::StructType::create("struct._ivar_list_t", IntTy, IntTy,
llvm::ArrayType::get(IvarnfABITy, 0));
IvarListnfABIPtrTy = llvm::PointerType::getUnqual(IvarListnfABITy);
ClassRonfABITy = llvm::StructType::create(
"struct._class_ro_t", IntTy, IntTy, IntTy, Int8PtrTy, Int8PtrTy,
MethodListnfABIPtrTy, ProtocolListnfABIPtrTy, IvarListnfABIPtrTy,
Int8PtrTy, PropertyListPtrTy);
llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy };
ImpnfABITy = llvm::FunctionType::get(ObjectPtrTy, params, false)
->getPointerTo();
ClassnfABITy = llvm::StructType::create(VMContext, "struct._class_t");
ClassnfABITy->setBody(llvm::PointerType::getUnqual(ClassnfABITy),
llvm::PointerType::getUnqual(ClassnfABITy), CachePtrTy,
llvm::PointerType::getUnqual(ImpnfABITy),
llvm::PointerType::getUnqual(ClassRonfABITy));
ClassnfABIPtrTy = llvm::PointerType::getUnqual(ClassnfABITy);
CategorynfABITy = llvm::StructType::create(
"struct._category_t", Int8PtrTy, ClassnfABIPtrTy, MethodListnfABIPtrTy,
MethodListnfABIPtrTy, ProtocolListnfABIPtrTy, PropertyListPtrTy,
PropertyListPtrTy, IntTy);
CodeGen::CodeGenTypes &Types = CGM.getTypes();
ASTContext &Ctx = CGM.getContext();
RecordDecl *RD = RecordDecl::Create(
Ctx, TagTypeKind::Struct, Ctx.getTranslationUnitDecl(), SourceLocation(),
SourceLocation(), &Ctx.Idents.get("_message_ref_t"));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(),
nullptr, Ctx.VoidPtrTy, nullptr, nullptr, false,
ICIS_NoInit));
RD->addDecl(FieldDecl::Create(Ctx, RD, SourceLocation(), SourceLocation(),
nullptr, Ctx.getObjCSelType(), nullptr, nullptr,
false, ICIS_NoInit));
RD->completeDefinition();
MessageRefCTy = Ctx.getTagDeclType(RD);
MessageRefCPtrTy = Ctx.getPointerType(MessageRefCTy);
MessageRefTy = cast<llvm::StructType>(Types.ConvertType(MessageRefCTy));
MessageRefPtrTy = llvm::PointerType::getUnqual(MessageRefTy);
SuperMessageRefTy = llvm::StructType::create("struct._super_message_ref_t",
ImpnfABITy, SelectorPtrTy);
SuperMessageRefPtrTy = llvm::PointerType::getUnqual(SuperMessageRefTy);
EHTypeTy = llvm::StructType::create("struct._objc_typeinfo",
llvm::PointerType::getUnqual(Int8PtrTy),
Int8PtrTy, ClassnfABIPtrTy);
EHTypePtrTy = llvm::PointerType::getUnqual(EHTypeTy);
}
llvm::Function *CGObjCNonFragileABIMac::ModuleInitFunction() {
FinishNonFragileABIModule();
return nullptr;
}
void CGObjCNonFragileABIMac::AddModuleClassList(
ArrayRef<llvm::GlobalValue *> Container, StringRef SymbolName,
StringRef SectionName) {
unsigned NumClasses = Container.size();
if (!NumClasses)
return;
SmallVector<llvm::Constant*, 8> Symbols(NumClasses);
for (unsigned i=0; i<NumClasses; i++)
Symbols[i] = Container[i];
llvm::Constant *Init =
llvm::ConstantArray::get(llvm::ArrayType::get(ObjCTypes.Int8PtrTy,
Symbols.size()),
Symbols);
assert((!CGM.getTriple().isOSBinFormatMachO() ||
SectionName.starts_with("__DATA")) &&
"SectionName expected to start with __DATA on MachO");
llvm::GlobalVariable *GV = new llvm::GlobalVariable(
CGM.getModule(), Init->getType(), false,
llvm::GlobalValue::PrivateLinkage, Init, SymbolName);
GV->setAlignment(CGM.getDataLayout().getABITypeAlign(Init->getType()));
GV->setSection(SectionName);
CGM.addCompilerUsedGlobal(GV);
}
void CGObjCNonFragileABIMac::FinishNonFragileABIModule() {
for (unsigned i=0, NumClasses=ImplementedClasses.size(); i<NumClasses; i++) {
const ObjCInterfaceDecl *ID = ImplementedClasses[i];
assert(ID);
if (ObjCImplementationDecl *IMP = ID->getImplementation())
if (ID->isWeakImported() && !IMP->isWeakImported()) {
DefinedClasses[i]->setLinkage(llvm::GlobalVariable::ExternalLinkage);
DefinedMetaClasses[i]->setLinkage(llvm::GlobalVariable::ExternalLinkage);
}
}
AddModuleClassList(DefinedClasses, "OBJC_LABEL_CLASS_$",
GetSectionName("__objc_classlist",
"regular,no_dead_strip"));
AddModuleClassList(DefinedNonLazyClasses, "OBJC_LABEL_NONLAZY_CLASS_$",
GetSectionName("__objc_nlclslist",
"regular,no_dead_strip"));
AddModuleClassList(DefinedCategories, "OBJC_LABEL_CATEGORY_$",
GetSectionName("__objc_catlist",
"regular,no_dead_strip"));
AddModuleClassList(DefinedStubCategories, "OBJC_LABEL_STUB_CATEGORY_$",
GetSectionName("__objc_catlist2",
"regular,no_dead_strip"));
AddModuleClassList(DefinedNonLazyCategories, "OBJC_LABEL_NONLAZY_CATEGORY_$",
GetSectionName("__objc_nlcatlist",
"regular,no_dead_strip"));
EmitImageInfo();
}
bool CGObjCNonFragileABIMac::isVTableDispatchedSelector(Selector Sel) {
switch (CGM.getCodeGenOpts().getObjCDispatchMethod()) {
case CodeGenOptions::Legacy:
return false;
case CodeGenOptions::NonLegacy:
return true;
case CodeGenOptions::Mixed:
break;
}
if (VTableDispatchMethods.empty()) {
VTableDispatchMethods.insert(GetNullarySelector("alloc"));
VTableDispatchMethods.insert(GetNullarySelector("class"));
VTableDispatchMethods.insert(GetNullarySelector("self"));
VTableDispatchMethods.insert(GetNullarySelector("isFlipped"));
VTableDispatchMethods.insert(GetNullarySelector("length"));
VTableDispatchMethods.insert(GetNullarySelector("count"));
if (CGM.getLangOpts().getGC() != LangOptions::GCOnly) {
VTableDispatchMethods.insert(GetNullarySelector("retain"));
VTableDispatchMethods.insert(GetNullarySelector("release"));
VTableDispatchMethods.insert(GetNullarySelector("autorelease"));
}
VTableDispatchMethods.insert(GetUnarySelector("allocWithZone"));
VTableDispatchMethods.insert(GetUnarySelector("isKindOfClass"));
VTableDispatchMethods.insert(GetUnarySelector("respondsToSelector"));
VTableDispatchMethods.insert(GetUnarySelector("objectForKey"));
VTableDispatchMethods.insert(GetUnarySelector("objectAtIndex"));
VTableDispatchMethods.insert(GetUnarySelector("isEqualToString"));
VTableDispatchMethods.insert(GetUnarySelector("isEqual"));
if (CGM.getLangOpts().getGC() != LangOptions::NonGC) {
VTableDispatchMethods.insert(GetNullarySelector("hash"));
VTableDispatchMethods.insert(GetUnarySelector("addObject"));
const IdentifierInfo *KeyIdents[] = {
&CGM.getContext().Idents.get("countByEnumeratingWithState"),
&CGM.getContext().Idents.get("objects"),
&CGM.getContext().Idents.get("count")};
VTableDispatchMethods.insert(
CGM.getContext().Selectors.getSelector(3, KeyIdents));
}
}
return VTableDispatchMethods.count(Sel);
}
llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer(
unsigned flags,
unsigned InstanceStart,
unsigned InstanceSize,
const ObjCImplementationDecl *ID) {
std::string ClassName = std::string(ID->getObjCRuntimeNameAsString());
CharUnits beginInstance = CharUnits::fromQuantity(InstanceStart);
CharUnits endInstance = CharUnits::fromQuantity(InstanceSize);
bool hasMRCWeak = false;
if (CGM.getLangOpts().ObjCAutoRefCount)
flags |= NonFragileABI_Class_CompiledByARC;
else if ((hasMRCWeak = hasMRCWeakIvars(CGM, ID)))
flags |= NonFragileABI_Class_HasMRCWeakIvars;
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ClassRonfABITy);
values.addInt(ObjCTypes.IntTy, flags);
values.addInt(ObjCTypes.IntTy, InstanceStart);
values.addInt(ObjCTypes.IntTy, InstanceSize);
values.add((flags & NonFragileABI_Class_Meta)
? GetIvarLayoutName(nullptr, ObjCTypes)
: BuildStrongIvarLayout(ID, beginInstance, endInstance));
values.add(GetClassName(ID->getObjCRuntimeNameAsString()));
SmallVector<const ObjCMethodDecl*, 16> methods;
if (flags & NonFragileABI_Class_Meta) {
for (const auto *MD : ID->class_methods())
if (!MD->isDirectMethod())
methods.push_back(MD);
} else {
for (const auto *MD : ID->instance_methods())
if (!MD->isDirectMethod())
methods.push_back(MD);
}
values.add(emitMethodList(ID->getObjCRuntimeNameAsString(),
(flags & NonFragileABI_Class_Meta)
? MethodListType::ClassMethods
: MethodListType::InstanceMethods,
methods));
const ObjCInterfaceDecl *OID = ID->getClassInterface();
assert(OID && "CGObjCNonFragileABIMac::BuildClassRoTInitializer");
values.add(EmitProtocolList("_OBJC_CLASS_PROTOCOLS_$_"
+ OID->getObjCRuntimeNameAsString(),
OID->all_referenced_protocol_begin(),
OID->all_referenced_protocol_end()));
if (flags & NonFragileABI_Class_Meta) {
values.addNullPointer(ObjCTypes.IvarListnfABIPtrTy);
values.add(GetIvarLayoutName(nullptr, ObjCTypes));
values.add(EmitPropertyList(
"_OBJC_$_CLASS_PROP_LIST_" + ID->getObjCRuntimeNameAsString(),
ID, ID->getClassInterface(), ObjCTypes, true));
} else {
values.add(EmitIvarList(ID));
values.add(BuildWeakIvarLayout(ID, beginInstance, endInstance, hasMRCWeak));
values.add(EmitPropertyList(
"_OBJC_$_PROP_LIST_" + ID->getObjCRuntimeNameAsString(),
ID, ID->getClassInterface(), ObjCTypes, false));
}
llvm::SmallString<64> roLabel;
llvm::raw_svector_ostream(roLabel)
<< ((flags & NonFragileABI_Class_Meta) ? "_OBJC_METACLASS_RO_$_"
: "_OBJC_CLASS_RO_$_")
<< ClassName;
return finishAndCreateGlobal(values, roLabel, CGM);
}
llvm::GlobalVariable *
CGObjCNonFragileABIMac::BuildClassObject(const ObjCInterfaceDecl *CI,
bool isMetaclass,
llvm::Constant *IsAGV,
llvm::Constant *SuperClassGV,
llvm::Constant *ClassRoGV,
bool HiddenVisibility) {
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ClassnfABITy);
values.add(IsAGV);
if (SuperClassGV) {
values.add(SuperClassGV);
} else {
values.addNullPointer(ObjCTypes.ClassnfABIPtrTy);
}
values.add(ObjCEmptyCacheVar);
values.add(ObjCEmptyVtableVar);
values.add(ClassRoGV);
llvm::GlobalVariable *GV =
cast<llvm::GlobalVariable>(GetClassGlobal(CI, isMetaclass, ForDefinition));
values.finishAndSetAsInitializer(GV);
if (CGM.getTriple().isOSBinFormatMachO())
GV->setSection("__DATA, __objc_data");
GV->setAlignment(CGM.getDataLayout().getABITypeAlign(ObjCTypes.ClassnfABITy));
if (!CGM.getTriple().isOSBinFormatCOFF())
if (HiddenVisibility)
GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
return GV;
}
bool CGObjCNonFragileABIMac::ImplementationIsNonLazy(
const ObjCImplDecl *OD) const {
return OD->getClassMethod(GetNullarySelector("load")) != nullptr ||
OD->getClassInterface()->hasAttr<ObjCNonLazyClassAttr>() ||
OD->hasAttr<ObjCNonLazyClassAttr>();
}
void CGObjCNonFragileABIMac::GetClassSizeInfo(const ObjCImplementationDecl *OID,
uint32_t &InstanceStart,
uint32_t &InstanceSize) {
const ASTRecordLayout &RL =
CGM.getContext().getASTObjCImplementationLayout(OID);
InstanceSize = RL.getDataSize().getQuantity();
if (!RL.getFieldCount())
InstanceStart = InstanceSize;
else
InstanceStart = RL.getFieldOffset(0) / CGM.getContext().getCharWidth();
}
static llvm::GlobalValue::DLLStorageClassTypes getStorage(CodeGenModule &CGM,
StringRef Name) {
IdentifierInfo &II = CGM.getContext().Idents.get(Name);
TranslationUnitDecl *TUDecl = CGM.getContext().getTranslationUnitDecl();
DeclContext *DC = TranslationUnitDecl::castToDeclContext(TUDecl);
const VarDecl *VD = nullptr;
for (const auto *Result : DC->lookup(&II))
if ((VD = dyn_cast<VarDecl>(Result)))
break;
if (!VD)
return llvm::GlobalValue::DLLImportStorageClass;
if (VD->hasAttr<DLLExportAttr>())
return llvm::GlobalValue::DLLExportStorageClass;
if (VD->hasAttr<DLLImportAttr>())
return llvm::GlobalValue::DLLImportStorageClass;
return llvm::GlobalValue::DefaultStorageClass;
}
void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) {
if (!ObjCEmptyCacheVar) {
ObjCEmptyCacheVar =
new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.CacheTy, false,
llvm::GlobalValue::ExternalLinkage, nullptr,
"_objc_empty_cache");
if (CGM.getTriple().isOSBinFormatCOFF())
ObjCEmptyCacheVar->setDLLStorageClass(getStorage(CGM, "_objc_empty_cache"));
const llvm::Triple &Triple = CGM.getTarget().getTriple();
if (Triple.isMacOSX() && Triple.isMacOSXVersionLT(10, 9))
ObjCEmptyVtableVar =
new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ImpnfABITy, false,
llvm::GlobalValue::ExternalLinkage, nullptr,
"_objc_empty_vtable");
else
ObjCEmptyVtableVar =
llvm::ConstantPointerNull::get(ObjCTypes.ImpnfABITy->getPointerTo());
}
uint32_t InstanceStart =
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ClassnfABITy);
uint32_t InstanceSize = InstanceStart;
uint32_t flags = NonFragileABI_Class_Meta;
llvm::Constant *SuperClassGV, *IsAGV;
const auto *CI = ID->getClassInterface();
assert(CI && "CGObjCNonFragileABIMac::GenerateClass - class is 0");
bool classIsHidden = (CGM.getTriple().isOSBinFormatCOFF())
? !CI->hasAttr<DLLExportAttr>()
: CI->getVisibility() == HiddenVisibility;
if (classIsHidden)
flags |= NonFragileABI_Class_Hidden;
if (ID->hasNonZeroConstructors() || ID->hasDestructors()) {
flags |= NonFragileABI_Class_HasCXXStructors;
if (!ID->hasNonZeroConstructors())
flags |= NonFragileABI_Class_HasCXXDestructorOnly;
}
if (!CI->getSuperClass()) {
flags |= NonFragileABI_Class_Root;
SuperClassGV = GetClassGlobal(CI, false, NotForDefinition);
IsAGV = GetClassGlobal(CI, true, NotForDefinition);
} else {
const ObjCInterfaceDecl *Root = ID->getClassInterface();
while (const ObjCInterfaceDecl *Super = Root->getSuperClass())
Root = Super;
const auto *Super = CI->getSuperClass();
IsAGV = GetClassGlobal(Root, true, NotForDefinition);
SuperClassGV = GetClassGlobal(Super, true, NotForDefinition);
}
llvm::GlobalVariable *CLASS_RO_GV =
BuildClassRoTInitializer(flags, InstanceStart, InstanceSize, ID);
llvm::GlobalVariable *MetaTClass =
BuildClassObject(CI, true,
IsAGV, SuperClassGV, CLASS_RO_GV, classIsHidden);
CGM.setGVProperties(MetaTClass, CI);
DefinedMetaClasses.push_back(MetaTClass);
flags = 0;
if (classIsHidden)
flags |= NonFragileABI_Class_Hidden;
if (ID->hasNonZeroConstructors() || ID->hasDestructors()) {
flags |= NonFragileABI_Class_HasCXXStructors;
if (!ID->hasNonZeroConstructors())
flags |= NonFragileABI_Class_HasCXXDestructorOnly;
}
if (hasObjCExceptionAttribute(CGM.getContext(), CI))
flags |= NonFragileABI_Class_Exception;
if (!CI->getSuperClass()) {
flags |= NonFragileABI_Class_Root;
SuperClassGV = nullptr;
} else {
const auto *Super = CI->getSuperClass();
SuperClassGV = GetClassGlobal(Super, false, NotForDefinition);
}
GetClassSizeInfo(ID, InstanceStart, InstanceSize);
CLASS_RO_GV =
BuildClassRoTInitializer(flags, InstanceStart, InstanceSize, ID);
llvm::GlobalVariable *ClassMD =
BuildClassObject(CI, false,
MetaTClass, SuperClassGV, CLASS_RO_GV, classIsHidden);
CGM.setGVProperties(ClassMD, CI);
DefinedClasses.push_back(ClassMD);
ImplementedClasses.push_back(CI);
if (ImplementationIsNonLazy(ID))
DefinedNonLazyClasses.push_back(ClassMD);
if (flags & NonFragileABI_Class_Exception)
(void) GetInterfaceEHType(CI, ForDefinition);
MethodDefinitions.clear();
}
llvm::Value *CGObjCNonFragileABIMac::GenerateProtocolRef(CodeGenFunction &CGF,
const ObjCProtocolDecl *PD) {
assert(!PD->isNonRuntimeProtocol() &&
"attempting to get a protocol ref to a static protocol.");
llvm::Constant *Init = GetOrEmitProtocol(PD);
std::string ProtocolName("_OBJC_PROTOCOL_REFERENCE_$_");
ProtocolName += PD->getObjCRuntimeNameAsString();
CharUnits Align = CGF.getPointerAlign();
llvm::GlobalVariable *PTGV = CGM.getModule().getGlobalVariable(ProtocolName);
if (PTGV)
return CGF.Builder.CreateAlignedLoad(PTGV->getValueType(), PTGV, Align);
PTGV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false,
llvm::GlobalValue::WeakAnyLinkage, Init,
ProtocolName);
PTGV->setSection(GetSectionName("__objc_protorefs",
"coalesced,no_dead_strip"));
PTGV->setVisibility(llvm::GlobalValue::HiddenVisibility);
PTGV->setAlignment(Align.getAsAlign());
if (!CGM.getTriple().isOSBinFormatMachO())
PTGV->setComdat(CGM.getModule().getOrInsertComdat(ProtocolName));
CGM.addUsedGlobal(PTGV);
return CGF.Builder.CreateAlignedLoad(PTGV->getValueType(), PTGV, Align);
}
void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) {
const ObjCInterfaceDecl *Interface = OCD->getClassInterface();
const char *Prefix = "_OBJC_$_CATEGORY_";
llvm::SmallString<64> ExtCatName(Prefix);
ExtCatName += Interface->getObjCRuntimeNameAsString();
ExtCatName += "_$_";
ExtCatName += OCD->getNameAsString();
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.CategorynfABITy);
values.add(GetClassName(OCD->getIdentifier()->getName()));
values.add(GetClassGlobal(Interface, false, NotForDefinition));
std::string listName =
(Interface->getObjCRuntimeNameAsString() + "_$_" + OCD->getName()).str();
SmallVector<const ObjCMethodDecl *, 16> instanceMethods;
SmallVector<const ObjCMethodDecl *, 8> classMethods;
for (const auto *MD : OCD->methods()) {
if (MD->isDirectMethod())
continue;
if (MD->isInstanceMethod()) {
instanceMethods.push_back(MD);
} else {
classMethods.push_back(MD);
}
}
auto instanceMethodList = emitMethodList(
listName, MethodListType::CategoryInstanceMethods, instanceMethods);
auto classMethodList = emitMethodList(
listName, MethodListType::CategoryClassMethods, classMethods);
values.add(instanceMethodList);
values.add(classMethodList);
bool isEmptyCategory =
instanceMethodList->isNullValue() && classMethodList->isNullValue();
const ObjCCategoryDecl *Category =
Interface->FindCategoryDeclaration(OCD->getIdentifier());
if (Category) {
SmallString<256> ExtName;
llvm::raw_svector_ostream(ExtName)
<< Interface->getObjCRuntimeNameAsString() << "_$_" << OCD->getName();
auto protocolList =
EmitProtocolList("_OBJC_CATEGORY_PROTOCOLS_$_" +
Interface->getObjCRuntimeNameAsString() + "_$_" +
Category->getName(),
Category->protocol_begin(), Category->protocol_end());
auto propertyList = EmitPropertyList("_OBJC_$_PROP_LIST_" + ExtName.str(),
OCD, Category, ObjCTypes, false);
auto classPropertyList =
EmitPropertyList("_OBJC_$_CLASS_PROP_LIST_" + ExtName.str(), OCD,
Category, ObjCTypes, true);
values.add(protocolList);
values.add(propertyList);
values.add(classPropertyList);
isEmptyCategory &= protocolList->isNullValue() &&
propertyList->isNullValue() &&
classPropertyList->isNullValue();
} else {
values.addNullPointer(ObjCTypes.ProtocolListnfABIPtrTy);
values.addNullPointer(ObjCTypes.PropertyListPtrTy);
values.addNullPointer(ObjCTypes.PropertyListPtrTy);
}
if (isEmptyCategory) {
values.abandon();
MethodDefinitions.clear();
return;
}
unsigned Size =
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.CategorynfABITy);
values.addInt(ObjCTypes.IntTy, Size);
llvm::GlobalVariable *GCATV =
finishAndCreateGlobal(values, ExtCatName.str(), CGM);
CGM.addCompilerUsedGlobal(GCATV);
if (Interface->hasAttr<ObjCClassStubAttr>())
DefinedStubCategories.push_back(GCATV);
else
DefinedCategories.push_back(GCATV);
if (ImplementationIsNonLazy(OCD))
DefinedNonLazyCategories.push_back(GCATV);
MethodDefinitions.clear();
}
void CGObjCNonFragileABIMac::emitMethodConstant(ConstantArrayBuilder &builder,
const ObjCMethodDecl *MD,
bool forProtocol) {
auto method = builder.beginStruct(ObjCTypes.MethodTy);
method.add(GetMethodVarName(MD->getSelector()));
method.add(GetMethodVarType(MD));
if (forProtocol) {
method.addNullPointer(ObjCTypes.Int8PtrProgramASTy);
} else {
llvm::Function *fn = GetMethodDefinition(MD);
assert(fn && "no definition for method?");
method.add(fn);
}
method.finishAndAddTo(builder);
}
llvm::Constant *
CGObjCNonFragileABIMac::emitMethodList(Twine name, MethodListType kind,
ArrayRef<const ObjCMethodDecl *> methods) {
if (methods.empty())
return llvm::Constant::getNullValue(ObjCTypes.MethodListnfABIPtrTy);
StringRef prefix;
bool forProtocol;
switch (kind) {
case MethodListType::CategoryInstanceMethods:
prefix = "_OBJC_$_CATEGORY_INSTANCE_METHODS_";
forProtocol = false;
break;
case MethodListType::CategoryClassMethods:
prefix = "_OBJC_$_CATEGORY_CLASS_METHODS_";
forProtocol = false;
break;
case MethodListType::InstanceMethods:
prefix = "_OBJC_$_INSTANCE_METHODS_";
forProtocol = false;
break;
case MethodListType::ClassMethods:
prefix = "_OBJC_$_CLASS_METHODS_";
forProtocol = false;
break;
case MethodListType::ProtocolInstanceMethods:
prefix = "_OBJC_$_PROTOCOL_INSTANCE_METHODS_";
forProtocol = true;
break;
case MethodListType::ProtocolClassMethods:
prefix = "_OBJC_$_PROTOCOL_CLASS_METHODS_";
forProtocol = true;
break;
case MethodListType::OptionalProtocolInstanceMethods:
prefix = "_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_";
forProtocol = true;
break;
case MethodListType::OptionalProtocolClassMethods:
prefix = "_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_";
forProtocol = true;
break;
}
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
unsigned Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.MethodTy);
values.addInt(ObjCTypes.IntTy, Size);
values.addInt(ObjCTypes.IntTy, methods.size());
auto methodArray = values.beginArray(ObjCTypes.MethodTy);
for (auto MD : methods)
emitMethodConstant(methodArray, MD, forProtocol);
methodArray.finishAndAddTo(values);
llvm::GlobalVariable *GV = finishAndCreateGlobal(values, prefix + name, CGM);
CGM.addCompilerUsedGlobal(GV);
return GV;
}
llvm::GlobalVariable *
CGObjCNonFragileABIMac::ObjCIvarOffsetVariable(const ObjCInterfaceDecl *ID,
const ObjCIvarDecl *Ivar) {
const ObjCInterfaceDecl *Container = Ivar->getContainingInterface();
llvm::SmallString<64> Name("OBJC_IVAR_$_");
Name += Container->getObjCRuntimeNameAsString();
Name += ".";
Name += Ivar->getName();
llvm::GlobalVariable *IvarOffsetGV = CGM.getModule().getGlobalVariable(Name);
if (!IvarOffsetGV) {
IvarOffsetGV =
new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.IvarOffsetVarTy,
false, llvm::GlobalValue::ExternalLinkage,
nullptr, Name.str());
if (CGM.getTriple().isOSBinFormatCOFF()) {
bool IsPrivateOrPackage =
Ivar->getAccessControl() == ObjCIvarDecl::Private ||
Ivar->getAccessControl() == ObjCIvarDecl::Package;
const ObjCInterfaceDecl *ContainingID = Ivar->getContainingInterface();
if (ContainingID->hasAttr<DLLImportAttr>())
IvarOffsetGV
->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
else if (ContainingID->hasAttr<DLLExportAttr>() && !IsPrivateOrPackage)
IvarOffsetGV
->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
}
}
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.IvarOffsetVarTy, Offset));
IvarOffsetGV->setAlignment(
CGM.getDataLayout().getABITypeAlign(ObjCTypes.IvarOffsetVarTy));
if (!CGM.getTriple().isOSBinFormatCOFF()) {
if (Ivar->getAccessControl() == ObjCIvarDecl::Private ||
Ivar->getAccessControl() == ObjCIvarDecl::Package ||
ID->getVisibility() == HiddenVisibility)
IvarOffsetGV->setVisibility(llvm::GlobalValue::HiddenVisibility);
else
IvarOffsetGV->setVisibility(llvm::GlobalValue::DefaultVisibility);
}
if (isClassLayoutKnownStatically(ID))
IvarOffsetGV->setConstant(true);
if (CGM.getTriple().isOSBinFormatMachO())
IvarOffsetGV->setSection("__DATA, __objc_ivar");
return IvarOffsetGV;
}
llvm::Constant *CGObjCNonFragileABIMac::EmitIvarList(
const ObjCImplementationDecl *ID) {
ConstantInitBuilder builder(CGM);
auto ivarList = builder.beginStruct();
ivarList.addInt(ObjCTypes.IntTy,
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.IvarnfABITy));
auto ivarCountSlot = ivarList.addPlaceholder();
auto ivars = ivarList.beginArray(ObjCTypes.IvarnfABITy);
const ObjCInterfaceDecl *OID = ID->getClassInterface();
assert(OID && "CGObjCNonFragileABIMac::EmitIvarList - null interface");
for (const ObjCIvarDecl *IVD = OID->all_declared_ivar_begin();
IVD; IVD = IVD->getNextIvar()) {
if (!IVD->getDeclName())
continue;
auto ivar = ivars.beginStruct(ObjCTypes.IvarnfABITy);
ivar.add(EmitIvarOffsetVar(ID->getClassInterface(), IVD,
ComputeIvarBaseOffset(CGM, ID, IVD)));
ivar.add(GetMethodVarName(IVD->getIdentifier()));
ivar.add(GetMethodVarType(IVD));
llvm::Type *FieldTy =
CGM.getTypes().ConvertTypeForMem(IVD->getType());
unsigned Size = CGM.getDataLayout().getTypeAllocSize(FieldTy);
unsigned Align = CGM.getContext().getPreferredTypeAlign(
IVD->getType().getTypePtr()) >> 3;
Align = llvm::Log2_32(Align);
ivar.addInt(ObjCTypes.IntTy, Align);
ivar.addInt(ObjCTypes.IntTy, Size);
ivar.finishAndAddTo(ivars);
}
if (ivars.empty()) {
ivars.abandon();
ivarList.abandon();
return llvm::Constant::getNullValue(ObjCTypes.IvarListnfABIPtrTy);
}
auto ivarCount = ivars.size();
ivars.finishAndAddTo(ivarList);
ivarList.fillPlaceholderWithInt(ivarCountSlot, ObjCTypes.IntTy, ivarCount);
const char *Prefix = "_OBJC_$_INSTANCE_VARIABLES_";
llvm::GlobalVariable *GV = finishAndCreateGlobal(
ivarList, Prefix + OID->getObjCRuntimeNameAsString(), CGM);
CGM.addCompilerUsedGlobal(GV);
return GV;
}
llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocolRef(
const ObjCProtocolDecl *PD) {
llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()];
assert(!PD->isNonRuntimeProtocol() &&
"attempting to GetOrEmit a non-runtime protocol");
if (!Entry) {
llvm::SmallString<64> Protocol;
llvm::raw_svector_ostream(Protocol) << "_OBJC_PROTOCOL_$_"
<< PD->getObjCRuntimeNameAsString();
Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolnfABITy,
false, llvm::GlobalValue::ExternalLinkage,
nullptr, Protocol);
if (!CGM.getTriple().isOSBinFormatMachO())
Entry->setComdat(CGM.getModule().getOrInsertComdat(Protocol));
}
return Entry;
}
llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol(
const ObjCProtocolDecl *PD) {
llvm::GlobalVariable *Entry = Protocols[PD->getIdentifier()];
if (Entry && Entry->hasInitializer())
return Entry;
assert(PD->hasDefinition() &&
"emitting protocol metadata without definition");
PD = PD->getDefinition();
auto methodLists = ProtocolMethodLists::get(PD);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ProtocolnfABITy);
values.addNullPointer(ObjCTypes.ObjectPtrTy);
values.add(GetClassName(PD->getObjCRuntimeNameAsString()));
values.add(EmitProtocolList("_OBJC_$_PROTOCOL_REFS_"
+ PD->getObjCRuntimeNameAsString(),
PD->protocol_begin(),
PD->protocol_end()));
values.add(methodLists.emitMethodList(this, PD,
ProtocolMethodLists::RequiredInstanceMethods));
values.add(methodLists.emitMethodList(this, PD,
ProtocolMethodLists::RequiredClassMethods));
values.add(methodLists.emitMethodList(this, PD,
ProtocolMethodLists::OptionalInstanceMethods));
values.add(methodLists.emitMethodList(this, PD,
ProtocolMethodLists::OptionalClassMethods));
values.add(EmitPropertyList(
"_OBJC_$_PROP_LIST_" + PD->getObjCRuntimeNameAsString(),
nullptr, PD, ObjCTypes, false));
uint32_t Size =
CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ProtocolnfABITy);
values.addInt(ObjCTypes.IntTy, Size);
values.addInt(ObjCTypes.IntTy, 0);
values.add(EmitProtocolMethodTypes("_OBJC_$_PROTOCOL_METHOD_TYPES_"
+ PD->getObjCRuntimeNameAsString(),
methodLists.emitExtendedTypesArray(this),
ObjCTypes));
values.addNullPointer(ObjCTypes.Int8PtrTy);
values.add(EmitPropertyList(
"_OBJC_$_CLASS_PROP_LIST_" + PD->getObjCRuntimeNameAsString(),
nullptr, PD, ObjCTypes, true));
if (Entry) {
Entry->setLinkage(llvm::GlobalValue::WeakAnyLinkage);
values.finishAndSetAsInitializer(Entry);
} else {
llvm::SmallString<64> symbolName;
llvm::raw_svector_ostream(symbolName)
<< "_OBJC_PROTOCOL_$_" << PD->getObjCRuntimeNameAsString();
Entry = values.finishAndCreateGlobal(symbolName, CGM.getPointerAlign(),
false,
llvm::GlobalValue::WeakAnyLinkage);
if (!CGM.getTriple().isOSBinFormatMachO())
Entry->setComdat(CGM.getModule().getOrInsertComdat(symbolName));
Protocols[PD->getIdentifier()] = Entry;
}
Entry->setVisibility(llvm::GlobalValue::HiddenVisibility);
CGM.addUsedGlobal(Entry);
llvm::SmallString<64> ProtocolRef;
llvm::raw_svector_ostream(ProtocolRef) << "_OBJC_LABEL_PROTOCOL_$_"
<< PD->getObjCRuntimeNameAsString();
llvm::GlobalVariable *PTGV =
new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ProtocolnfABIPtrTy,
false, llvm::GlobalValue::WeakAnyLinkage, Entry,
ProtocolRef);
if (!CGM.getTriple().isOSBinFormatMachO())
PTGV->setComdat(CGM.getModule().getOrInsertComdat(ProtocolRef));
PTGV->setAlignment(
CGM.getDataLayout().getABITypeAlign(ObjCTypes.ProtocolnfABIPtrTy));
PTGV->setSection(GetSectionName("__objc_protolist",
"coalesced,no_dead_strip"));
PTGV->setVisibility(llvm::GlobalValue::HiddenVisibility);
CGM.addUsedGlobal(PTGV);
return Entry;
}
llvm::Constant *
CGObjCNonFragileABIMac::EmitProtocolList(Twine Name,
ObjCProtocolDecl::protocol_iterator begin,
ObjCProtocolDecl::protocol_iterator end) {
auto Protocols = GetRuntimeProtocolList(begin, end);
if (Protocols.empty())
return llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy);
SmallVector<llvm::Constant *, 16> ProtocolRefs;
ProtocolRefs.reserve(Protocols.size());
for (const auto *PD : Protocols)
ProtocolRefs.push_back(GetProtocolRef(PD));
if (ProtocolRefs.size() == 0)
return llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy);
SmallString<256> TmpName;
Name.toVector(TmpName);
llvm::GlobalVariable *GV =
CGM.getModule().getGlobalVariable(TmpName.str(), true);
if (GV)
return GV;
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
auto countSlot = values.addPlaceholder();
auto array = values.beginArray(ObjCTypes.ProtocolnfABIPtrTy);
for (auto const &proto : ProtocolRefs)
array.add(proto);
auto count = array.size();
array.addNullPointer(ObjCTypes.ProtocolnfABIPtrTy);
array.finishAndAddTo(values);
values.fillPlaceholderWithInt(countSlot, ObjCTypes.LongTy, count);
GV = finishAndCreateGlobal(values, Name, CGM);
CGM.addCompilerUsedGlobal(GV);
return GV;
}
LValue CGObjCNonFragileABIMac::EmitObjCValueForIvar(
CodeGen::CodeGenFunction &CGF,
QualType ObjectTy,
llvm::Value *BaseValue,
const ObjCIvarDecl *Ivar,
unsigned CVRQualifiers) {
ObjCInterfaceDecl *ID = ObjectTy->castAs<ObjCObjectType>()->getInterface();
llvm::Value *Offset = EmitIvarOffset(CGF, ID, Ivar);
return EmitValueForIvarAtOffset(CGF, ID, BaseValue, Ivar, CVRQualifiers,
Offset);
}
llvm::Value *
CGObjCNonFragileABIMac::EmitIvarOffset(CodeGen::CodeGenFunction &CGF,
const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar) {
llvm::Value *IvarOffsetValue;
if (isClassLayoutKnownStatically(Interface)) {
IvarOffsetValue = llvm::ConstantInt::get(
ObjCTypes.IvarOffsetVarTy,
ComputeIvarBaseOffset(CGM, Interface->getImplementation(), Ivar));
} else {
llvm::GlobalVariable *GV = ObjCIvarOffsetVariable(Interface, Ivar);
IvarOffsetValue =
CGF.Builder.CreateAlignedLoad(GV->getValueType(), GV,
CGF.getSizeAlign(), "ivar");
if (IsIvarOffsetKnownIdempotent(CGF, Ivar))
cast<llvm::LoadInst>(IvarOffsetValue)
->setMetadata(llvm::LLVMContext::MD_invariant_load,
llvm::MDNode::get(VMContext, std::nullopt));
}
if (ObjCTypes.IvarOffsetVarTy == ObjCTypes.IntTy)
IvarOffsetValue = CGF.Builder.CreateIntCast(
IvarOffsetValue, ObjCTypes.LongTy, true, "ivar.conv");
return IvarOffsetValue;
}
static void appendSelectorForMessageRefTable(std::string &buffer,
Selector selector) {
if (selector.isUnarySelector()) {
buffer += selector.getNameForSlot(0);
return;
}
for (unsigned i = 0, e = selector.getNumArgs(); i != e; ++i) {
buffer += selector.getNameForSlot(i);
buffer += '_';
}
}
RValue
CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF,
ReturnValueSlot returnSlot,
QualType resultType,
Selector selector,
llvm::Value *arg0,
QualType arg0Type,
bool isSuper,
const CallArgList &formalArgs,
const ObjCMethodDecl *method) {
CallArgList args;
if (!isSuper)
arg0 = CGF.Builder.CreateBitCast(arg0, ObjCTypes.ObjectPtrTy);
args.add(RValue::get(arg0), arg0Type);
args.add(RValue::get(nullptr), ObjCTypes.MessageRefCPtrTy);
args.insert(args.end(), formalArgs.begin(), formalArgs.end());
MessageSendInfo MSI = getMessageSendInfo(method, resultType, args);
NullReturnState nullReturn;
llvm::FunctionCallee fn = nullptr;
std::string messageRefName("_");
if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) {
if (isSuper) {
fn = ObjCTypes.getMessageSendSuper2StretFixupFn();
messageRefName += "objc_msgSendSuper2_stret_fixup";
} else {
nullReturn.init(CGF, arg0);
fn = ObjCTypes.getMessageSendStretFixupFn();
messageRefName += "objc_msgSend_stret_fixup";
}
} else if (!isSuper && CGM.ReturnTypeUsesFPRet(resultType)) {
fn = ObjCTypes.getMessageSendFpretFixupFn();
messageRefName += "objc_msgSend_fpret_fixup";
} else {
if (isSuper) {
fn = ObjCTypes.getMessageSendSuper2FixupFn();
messageRefName += "objc_msgSendSuper2_fixup";
} else {
fn = ObjCTypes.getMessageSendFixupFn();
messageRefName += "objc_msgSend_fixup";
}
}
assert(fn && "CGObjCNonFragileABIMac::EmitMessageSend");
messageRefName += '_';
appendSelectorForMessageRefTable(messageRefName, selector);
llvm::GlobalVariable *messageRef
= CGM.getModule().getGlobalVariable(messageRefName);
if (!messageRef) {
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct();
values.add(cast<llvm::Constant>(fn.getCallee()));
values.add(GetMethodVarName(selector));
messageRef = values.finishAndCreateGlobal(messageRefName,
CharUnits::fromQuantity(16),
false,
llvm::GlobalValue::WeakAnyLinkage);
messageRef->setVisibility(llvm::GlobalValue::HiddenVisibility);
messageRef->setSection(GetSectionName("__objc_msgrefs", "coalesced"));
}
bool requiresnullCheck = false;
if (CGM.getLangOpts().ObjCAutoRefCount && method)
for (const auto *ParamDecl : method->parameters()) {
if (ParamDecl->isDestroyedInCallee()) {
if (!nullReturn.NullBB)
nullReturn.init(CGF, arg0);
requiresnullCheck = true;
break;
}
}
Address mref =
Address(CGF.Builder.CreateBitCast(messageRef, ObjCTypes.MessageRefPtrTy),
ObjCTypes.MessageRefTy, CGF.getPointerAlign());
args[1].setRValue(RValue::get(mref, CGF));
Address calleeAddr = CGF.Builder.CreateStructGEP(mref, 0);
llvm::Value *calleePtr = CGF.Builder.CreateLoad(calleeAddr, "msgSend_fn");
calleePtr = CGF.Builder.CreateBitCast(calleePtr, MSI.MessengerType);
CGCallee callee(CGCalleeInfo(), calleePtr);
RValue result = CGF.EmitCall(MSI.CallInfo, callee, returnSlot, args);
return nullReturn.complete(CGF, returnSlot, result, resultType, formalArgs,
requiresnullCheck ? method : nullptr);
}
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 isVTableDispatchedSelector(Sel)
? EmitVTableMessageSend(CGF, Return, ResultType, Sel,
Receiver, CGF.getContext().getObjCIdType(),
false, CallArgs, Method)
: EmitMessageSend(CGF, Return, ResultType, Sel,
Receiver, CGF.getContext().getObjCIdType(),
false, CallArgs, Method, Class, ObjCTypes);
}
llvm::Constant *
CGObjCNonFragileABIMac::GetClassGlobal(const ObjCInterfaceDecl *ID,
bool metaclass,
ForDefinition_t isForDefinition) {
auto prefix =
(metaclass ? getMetaclassSymbolPrefix() : getClassSymbolPrefix());
return GetClassGlobal((prefix + ID->getObjCRuntimeNameAsString()).str(),
isForDefinition,
ID->isWeakImported(),
!isForDefinition
&& CGM.getTriple().isOSBinFormatCOFF()
&& ID->hasAttr<DLLImportAttr>());
}
llvm::Constant *
CGObjCNonFragileABIMac::GetClassGlobal(StringRef Name,
ForDefinition_t IsForDefinition,
bool Weak, bool DLLImport) {
llvm::GlobalValue::LinkageTypes L =
Weak ? llvm::GlobalValue::ExternalWeakLinkage
: llvm::GlobalValue::ExternalLinkage;
llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name);
if (!GV || GV->getValueType() != ObjCTypes.ClassnfABITy) {
auto *NewGV = new llvm::GlobalVariable(ObjCTypes.ClassnfABITy, false, L,
nullptr, Name);
if (DLLImport)
NewGV->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
if (GV) {
GV->replaceAllUsesWith(NewGV);
GV->eraseFromParent();
}
GV = NewGV;
CGM.getModule().insertGlobalVariable(GV);
}
assert(GV->getLinkage() == L);
return GV;
}
llvm::Constant *
CGObjCNonFragileABIMac::GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID) {
llvm::Constant *ClassGV = GetClassGlobal(ID, false,
NotForDefinition);
if (!ID->hasAttr<ObjCClassStubAttr>())
return ClassGV;
ClassGV = llvm::ConstantExpr::getPointerCast(ClassGV, ObjCTypes.Int8PtrTy);
auto *Idx = llvm::ConstantInt::get(CGM.Int32Ty, 1);
return llvm::ConstantExpr::getGetElementPtr(CGM.Int8Ty, ClassGV, Idx);
}
llvm::Value *
CGObjCNonFragileABIMac::EmitLoadOfClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID,
llvm::GlobalVariable *Entry) {
if (ID && ID->hasAttr<ObjCClassStubAttr>()) {
return CGF.EmitRuntimeCall(
ObjCTypes.getLoadClassrefFn(), Entry, "load_classref_result");
}
CharUnits Align = CGF.getPointerAlign();
return CGF.Builder.CreateAlignedLoad(Entry->getValueType(), Entry, Align);
}
llvm::Value *
CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF,
IdentifierInfo *II,
const ObjCInterfaceDecl *ID) {
llvm::GlobalVariable *&Entry = ClassReferences[II];
if (!Entry) {
llvm::Constant *ClassGV;
if (ID) {
ClassGV = GetClassGlobalForClassRef(ID);
} else {
ClassGV = GetClassGlobal((getClassSymbolPrefix() + II->getName()).str(),
NotForDefinition);
assert(ClassGV->getType() == ObjCTypes.ClassnfABIPtrTy &&
"classref was emitted with the wrong type?");
}
std::string SectionName =
GetSectionName("__objc_classrefs", "regular,no_dead_strip");
Entry = new llvm::GlobalVariable(
CGM.getModule(), ClassGV->getType(), false,
getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV,
"OBJC_CLASSLIST_REFERENCES_$_");
Entry->setAlignment(CGF.getPointerAlign().getAsAlign());
if (!ID || !ID->hasAttr<ObjCClassStubAttr>())
Entry->setSection(SectionName);
CGM.addCompilerUsedGlobal(Entry);
}
return EmitLoadOfClassRef(CGF, ID, Entry);
}
llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) {
if (ID->hasAttr<ObjCRuntimeVisibleAttr>())
return EmitClassRefViaRuntime(CGF, ID, ObjCTypes);
return EmitClassRefFromId(CGF, ID->getIdentifier(), ID);
}
llvm::Value *CGObjCNonFragileABIMac::EmitNSAutoreleasePoolClassRef(
CodeGenFunction &CGF) {
IdentifierInfo *II = &CGM.getContext().Idents.get("NSAutoreleasePool");
return EmitClassRefFromId(CGF, II, nullptr);
}
llvm::Value *
CGObjCNonFragileABIMac::EmitSuperClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) {
llvm::GlobalVariable *&Entry = SuperClassReferences[ID->getIdentifier()];
if (!Entry) {
llvm::Constant *ClassGV = GetClassGlobalForClassRef(ID);
std::string SectionName =
GetSectionName("__objc_superrefs", "regular,no_dead_strip");
Entry = new llvm::GlobalVariable(CGM.getModule(), ClassGV->getType(), false,
llvm::GlobalValue::PrivateLinkage, ClassGV,
"OBJC_CLASSLIST_SUP_REFS_$_");
Entry->setAlignment(CGF.getPointerAlign().getAsAlign());
Entry->setSection(SectionName);
CGM.addCompilerUsedGlobal(Entry);
}
return EmitLoadOfClassRef(CGF, ID, Entry);
}
llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID,
bool Weak) {
CharUnits Align = CGF.getPointerAlign();
llvm::GlobalVariable * &Entry = MetaClassReferences[ID->getIdentifier()];
if (!Entry) {
auto MetaClassGV = GetClassGlobal(ID, true, NotForDefinition);
std::string SectionName =
GetSectionName("__objc_superrefs", "regular,no_dead_strip");
Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy,
false, llvm::GlobalValue::PrivateLinkage,
MetaClassGV, "OBJC_CLASSLIST_SUP_REFS_$_");
Entry->setAlignment(Align.getAsAlign());
Entry->setSection(SectionName);
CGM.addCompilerUsedGlobal(Entry);
}
return CGF.Builder.CreateAlignedLoad(ObjCTypes.ClassnfABIPtrTy, Entry, Align);
}
llvm::Value *CGObjCNonFragileABIMac::GetClass(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) {
if (ID->isWeakImported()) {
auto ClassGV = GetClassGlobal(ID, false, NotForDefinition);
(void)ClassGV;
assert(!isa<llvm::GlobalVariable>(ClassGV) ||
cast<llvm::GlobalVariable>(ClassGV)->hasExternalWeakLinkage());
}
return EmitClassRef(CGF, ID);
}
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) {
RawAddress ObjCSuper = CGF.CreateTempAlloca(
ObjCTypes.SuperTy, CGF.getPointerAlign(), "objc_super");
llvm::Value *ReceiverAsObject =
CGF.Builder.CreateBitCast(Receiver, ObjCTypes.ObjectPtrTy);
CGF.Builder.CreateStore(ReceiverAsObject,
CGF.Builder.CreateStructGEP(ObjCSuper, 0));
llvm::Value *Target;
if (IsClassMessage)
Target = EmitMetaClassRef(CGF, Class, Class->isWeakImported());
else
Target = EmitSuperClassRef(CGF, Class);
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 (isVTableDispatchedSelector(Sel))
? EmitVTableMessageSend(CGF, Return, ResultType, Sel,
ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
true, CallArgs, Method)
: EmitMessageSend(CGF, Return, ResultType, Sel,
ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
true, CallArgs, Method, Class, ObjCTypes);
}
llvm::Value *CGObjCNonFragileABIMac::EmitSelector(CodeGenFunction &CGF,
Selector Sel) {
Address Addr = EmitSelectorAddr(Sel);
llvm::LoadInst* LI = CGF.Builder.CreateLoad(Addr);
LI->setMetadata(llvm::LLVMContext::MD_invariant_load,
llvm::MDNode::get(VMContext, std::nullopt));
return LI;
}
ConstantAddress CGObjCNonFragileABIMac::EmitSelectorAddr(Selector Sel) {
llvm::GlobalVariable *&Entry = SelectorReferences[Sel];
CharUnits Align = CGM.getPointerAlign();
if (!Entry) {
std::string SectionName =
GetSectionName("__objc_selrefs", "literal_pointers,no_dead_strip");
Entry = new llvm::GlobalVariable(
CGM.getModule(), ObjCTypes.SelectorPtrTy, false,
getLinkageTypeForObjCMetadata(CGM, SectionName), GetMethodVarName(Sel),
"OBJC_SELECTOR_REFERENCES_");
Entry->setExternallyInitialized(true);
Entry->setSection(SectionName);
Entry->setAlignment(Align.getAsAlign());
CGM.addCompilerUsedGlobal(Entry);
}
return ConstantAddress(Entry, ObjCTypes.SelectorPtrTy, Align);
}
void CGObjCNonFragileABIMac::EmitObjCIvarAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src,
Address dst,
llvm::Value *ivarOffset) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().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);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal, ivarOffset};
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignIvarFn(), args);
}
void CGObjCNonFragileABIMac::EmitObjCStrongCastAssign(
CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().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);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal};
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignStrongCastFn(),
args, "weakassign");
}
void CGObjCNonFragileABIMac::EmitGCMemmoveCollectable(
CodeGen::CodeGenFunction &CGF, Address DestPtr, Address SrcPtr,
llvm::Value *Size) {
llvm::Value *args[] = {DestPtr.emitRawPointer(CGF),
SrcPtr.emitRawPointer(CGF), Size};
CGF.EmitNounwindRuntimeCall(ObjCTypes.GcMemmoveCollectableFn(), args);
}
llvm::Value * CGObjCNonFragileABIMac::EmitObjCWeakRead(
CodeGen::CodeGenFunction &CGF,
Address AddrWeakObj) {
llvm::Type *DestTy = AddrWeakObj.getElementType();
llvm::Value *AddrWeakObjVal = CGF.Builder.CreateBitCast(
AddrWeakObj.emitRawPointer(CGF), ObjCTypes.PtrObjectPtrTy);
llvm::Value *read_weak =
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcReadWeakFn(),
AddrWeakObjVal, "weakread");
read_weak = CGF.Builder.CreateBitCast(read_weak, DestTy);
return read_weak;
}
void CGObjCNonFragileABIMac::EmitObjCWeakAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().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);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal};
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignWeakFn(),
args, "weakassign");
}
void CGObjCNonFragileABIMac::EmitObjCGlobalAssign(CodeGen::CodeGenFunction &CGF,
llvm::Value *src, Address dst,
bool threadlocal) {
llvm::Type * SrcTy = src->getType();
if (!isa<llvm::PointerType>(SrcTy)) {
unsigned Size = CGM.getDataLayout().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);
llvm::Value *dstVal = CGF.Builder.CreateBitCast(dst.emitRawPointer(CGF),
ObjCTypes.PtrObjectPtrTy);
llvm::Value *args[] = {src, dstVal};
if (!threadlocal)
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignGlobalFn(),
args, "globalassign");
else
CGF.EmitNounwindRuntimeCall(ObjCTypes.getGcAssignThreadLocalFn(),
args, "threadlocalassign");
}
void
CGObjCNonFragileABIMac::EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtSynchronizedStmt &S) {
EmitAtSynchronizedStmt(CGF, S, ObjCTypes.getSyncEnterFn(),
ObjCTypes.getSyncExitFn());
}
llvm::Constant *
CGObjCNonFragileABIMac::GetEHType(QualType T) {
if (T->isObjCIdType() || T->isObjCQualifiedIdType()) {
auto *IDEHType = CGM.getModule().getGlobalVariable("OBJC_EHTYPE_id");
if (!IDEHType) {
IDEHType =
new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy, false,
llvm::GlobalValue::ExternalLinkage, nullptr,
"OBJC_EHTYPE_id");
if (CGM.getTriple().isOSBinFormatCOFF())
IDEHType->setDLLStorageClass(getStorage(CGM, "OBJC_EHTYPE_id"));
}
return IDEHType;
}
const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
assert(PT && "Invalid @catch type.");
const ObjCInterfaceType *IT = PT->getInterfaceType();
assert(IT && "Invalid @catch type.");
return GetInterfaceEHType(IT->getDecl(), NotForDefinition);
}
void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtTryStmt &S) {
EmitTryCatchStmt(CGF, S, ObjCTypes.getObjCBeginCatchFn(),
ObjCTypes.getObjCEndCatchFn(),
ObjCTypes.getExceptionRethrowFn());
}
void CGObjCNonFragileABIMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtThrowStmt &S,
bool ClearInsertionPoint) {
if (const Expr *ThrowExpr = S.getThrowExpr()) {
llvm::Value *Exception = CGF.EmitObjCThrowOperand(ThrowExpr);
Exception = CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy);
llvm::CallBase *Call =
CGF.EmitRuntimeCallOrInvoke(ObjCTypes.getExceptionThrowFn(), Exception);
Call->setDoesNotReturn();
} else {
llvm::CallBase *Call =
CGF.EmitRuntimeCallOrInvoke(ObjCTypes.getExceptionRethrowFn());
Call->setDoesNotReturn();
}
CGF.Builder.CreateUnreachable();
if (ClearInsertionPoint)
CGF.Builder.ClearInsertionPoint();
}
llvm::Constant *
CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID,
ForDefinition_t IsForDefinition) {
llvm::GlobalVariable * &Entry = EHTypeReferences[ID->getIdentifier()];
StringRef ClassName = ID->getObjCRuntimeNameAsString();
if (!IsForDefinition) {
if (Entry)
return Entry;
if (hasObjCExceptionAttribute(CGM.getContext(), ID)) {
std::string EHTypeName = ("OBJC_EHTYPE_$_" + ClassName).str();
Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy,
false, llvm::GlobalValue::ExternalLinkage,
nullptr, EHTypeName);
CGM.setGVProperties(Entry, ID);
return Entry;
}
}
assert((!Entry || !Entry->hasInitializer()) && "Duplicate EHType definition");
std::string VTableName = "objc_ehtype_vtable";
auto *VTableGV = CGM.getModule().getGlobalVariable(VTableName);
if (!VTableGV) {
VTableGV =
new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.Int8PtrTy, false,
llvm::GlobalValue::ExternalLinkage, nullptr,
VTableName);
if (CGM.getTriple().isOSBinFormatCOFF())
VTableGV->setDLLStorageClass(getStorage(CGM, VTableName));
}
llvm::Value *VTableIdx = llvm::ConstantInt::get(CGM.Int32Ty, 2);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.EHTypeTy);
values.add(
llvm::ConstantExpr::getInBoundsGetElementPtr(VTableGV->getValueType(),
VTableGV, VTableIdx));
values.add(GetClassName(ClassName));
values.add(GetClassGlobal(ID, false, NotForDefinition));
llvm::GlobalValue::LinkageTypes L = IsForDefinition
? llvm::GlobalValue::ExternalLinkage
: llvm::GlobalValue::WeakAnyLinkage;
if (Entry) {
values.finishAndSetAsInitializer(Entry);
Entry->setAlignment(CGM.getPointerAlign().getAsAlign());
} else {
Entry = values.finishAndCreateGlobal("OBJC_EHTYPE_$_" + ClassName,
CGM.getPointerAlign(),
false,
L);
if (hasObjCExceptionAttribute(CGM.getContext(), ID))
CGM.setGVProperties(Entry, ID);
}
assert(Entry->getLinkage() == L);
if (!CGM.getTriple().isOSBinFormatCOFF())
if (ID->getVisibility() == HiddenVisibility)
Entry->setVisibility(llvm::GlobalValue::HiddenVisibility);
if (IsForDefinition)
if (CGM.getTriple().isOSBinFormatMachO())
Entry->setSection("__DATA,__objc_const");
return Entry;
}
CodeGen::CGObjCRuntime *
CodeGen::CreateMacObjCRuntime(CodeGen::CodeGenModule &CGM) {
switch (CGM.getLangOpts().ObjCRuntime.getKind()) {
case ObjCRuntime::FragileMacOSX:
return new CGObjCMac(CGM);
case ObjCRuntime::MacOSX:
case ObjCRuntime::iOS:
case ObjCRuntime::WatchOS:
return new CGObjCNonFragileABIMac(CGM);
case ObjCRuntime::GNUstep:
case ObjCRuntime::GCC:
case ObjCRuntime::ObjFW:
llvm_unreachable("these runtimes are not Mac runtimes");
}
llvm_unreachable("bad runtime");
}