From 8225ff0a8686501f30bd66bfcf6b867975445954 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 12 Nov 2024 06:09:58 +0100 Subject: [PATCH 01/67] WIP: Objective-C support --- gen/abi/aarch64.cpp | 4 +- gen/abi/abi.cpp | 2 +- gen/abi/abi.h | 2 +- gen/abi/arm.cpp | 4 +- gen/abi/x86-64.cpp | 22 ++---- gen/abi/x86.cpp | 6 +- gen/declarations.cpp | 8 ++ gen/functions.cpp | 2 +- gen/llvmhelpers.cpp | 12 +++ gen/objcgen.cpp | 184 ++++++++++++++++++++++++++----------------- gen/objcgen.h | 48 +++++++++-- 11 files changed, 194 insertions(+), 100 deletions(-) diff --git a/gen/abi/aarch64.cpp b/gen/abi/aarch64.cpp index 285bc56668b..598ca1d6816 100644 --- a/gen/abi/aarch64.cpp +++ b/gen/abi/aarch64.cpp @@ -176,9 +176,9 @@ struct AArch64TargetABI : TargetABI { return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); } - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override { + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { // see objc/message.h for objc_msgSend selection rules - return "objc_msgSend"; + return superCall ? "objc_msgSendSuper" : "objc_msgSend"; } }; diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index c3f61f18daf..99cc18457c6 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -207,7 +207,7 @@ Type *TargetABI::vaListType() { ////////////////////////////////////////////////////////////////////////////// -const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty) { +const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) { llvm_unreachable("Unknown Objective-C ABI"); } diff --git a/gen/abi/abi.h b/gen/abi/abi.h index c1f69046a1d..4f8c2362914 100644 --- a/gen/abi/abi.h +++ b/gen/abi/abi.h @@ -171,7 +171,7 @@ struct TargetABI { virtual Type *vaListType(); /// Returns Objective-C message send function - virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty); + virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall); /***** Static Helpers *****/ diff --git a/gen/abi/arm.cpp b/gen/abi/arm.cpp index 8821ff8a340..d46b5f794cf 100644 --- a/gen/abi/arm.cpp +++ b/gen/abi/arm.cpp @@ -123,12 +123,12 @@ struct ArmTargetABI : TargetABI { return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); } - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override { + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { // see objc/message.h for objc_msgSend selection rules if (fty.arg_sret) { return "objc_msgSend_stret"; } - return "objc_msgSend"; + return superCall ? "objc_msgSendSuper" : "objc_msgSend"; } }; diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index 1a49791df5a..18a26b3c88c 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -382,21 +382,15 @@ Type *X86_64TargetABI::vaListType() { TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag"))); } -const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, - IrFuncTy &fty) { +const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) { // see objc/message.h for objc_msgSend selection rules + assert(isDarwin); if (fty.arg_sret) { - return "objc_msgSend_stret"; + return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } - if (ret) { - // complex long double return - if (ret->ty == TY::Tcomplex80) { - return "objc_msgSend_fp2ret"; - } - // long double return - if (ret->ty == TY::Tfloat80 || ret->ty == TY::Timaginary80) { - return "objc_msgSend_fpret"; - } + // float, double, long double return + if (ret && ret->isfloating() && !ret->iscomplex()) { + return "objc_msgSend_fpret"; } - return "objc_msgSend"; -} + return superCall ? "objc_msgSendSuper" : "objc_msgSend"; +} \ No newline at end of file diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index cd9acbe76f2..f188590612a 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -273,17 +273,17 @@ struct X86TargetABI : TargetABI { } } - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override { + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { // see objc/message.h for objc_msgSend selection rules assert(isDarwin); if (fty.arg_sret) { - return "objc_msgSend_stret"; + return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } // float, double, long double return if (ret && ret->isfloating() && !ret->iscomplex()) { return "objc_msgSend_fpret"; } - return "objc_msgSend"; + return superCall ? "objc_msgSendSuper" : "objc_msgSend"; } }; diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 31f1ec79661..9b66d9a2bab 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -109,6 +109,10 @@ class CodegenVisitor : public Visitor { m->accept(this); } + // Objective-C protocols don't have TypeInfo. + if(decl->classKind == ClassKind::objc) + return; + // Emit TypeInfo. IrClass *ir = getIrAggr(decl); if (!ir->suppressTypeInfo()) { @@ -205,6 +209,10 @@ class CodegenVisitor : public Visitor { m->accept(this); } + // TODO: Support creating objective-c exposed classes? + if (decl->classKind == ClassKind::objc) + return; + IrClass *ir = getIrAggr(decl); ir->getInitSymbol(/*define=*/true); diff --git a/gen/functions.cpp b/gen/functions.cpp index ad7fc21456b..0bde089db0f 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -140,7 +140,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, } bool hasObjCSelector = false; - if (fd && fd->_linkage == LINK::objc && thistype) { + if (fd && fd->_linkage == LINK::objc) { if (fd->objc.selector) { hasObjCSelector = true; } else if (fd->parent->isClassDeclaration()) { diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 0cc63e70b8f..751043480cd 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1841,6 +1841,18 @@ DLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, // ourselves, DtoType below would be enough. DtoResolveDsymbol(ad); + if (ad->classKind == ClassKind::objc) { + auto tHandle = LLType::getInt64Ty(gIR->context()); + auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarOffset(*ad->isClassDeclaration(), *vd, flase)); + + // Offset is now stored in tOffset. + LLValue *ptr = src; + ptr = DtoBitCast(ptr, getVoidPtrType()); + ptr = DtoGEP1(llvm::Type::getInt8Ty(gIR->context()), ptr, tOffset); + + return new DLValue(vd->type, ptr); + } + // Look up field to index or offset to apply. auto irTypeAggr = getIrType(ad->type)->isAggr(); assert(irTypeAggr); diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index a85ee68dd80..9342f8f8b60 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -15,24 +15,22 @@ #include "dmd/objc.h" #include "gen/irstate.h" -namespace { -enum ABI { none = 0, fragile = 1, nonFragile = 2 }; -ABI abi = nonFragile; -} +#define OBJC_CLASS_LIST = "__DATA,__objc_classlist"; +#define OBJC_PROTOCOL_LIST = "__DATA,__objc_protolist"; bool objc_isSupported(const llvm::Triple &triple) { if (triple.isOSDarwin()) { + // Objective-C only supported on Darwin at this time + // Additionally only Objective-C 2 is supported. switch (triple.getArch()) { case llvm::Triple::aarch64: // arm64 iOS, tvOS case llvm::Triple::arm: // armv6 iOS case llvm::Triple::thumb: // thumbv7 iOS, watchOS case llvm::Triple::x86_64: // OSX, iOS, tvOS sim - abi = nonFragile; return true; case llvm::Triple::x86: // OSX, iOS, watchOS sim - abi = fragile; - return true; + return false; default: break; } @@ -40,55 +38,100 @@ bool objc_isSupported(const llvm::Triple &triple) { return false; } +llvm::GlobalVariable *getGlobal( + llvm::Module& module, + llvm::StringRef& name, + llvm::Type* type = nullptr +) { + if (type == nullptr) + type = llvm::PointerType::get(llvm::Type::getVoidTy(module.getContext()), 0); + + auto var = new LLGlobalVariable( + module, + type, + false, + LLGlobalValue::ExternalLinkage, + nullptr, + name, + nullptr, + LLGlobalVariable::NotThreadLocal, + 0, + true + ); + return var; +} + +llvm::GlobalVariable *getGlobalWithBytes( + llvm::Module& module, + llvm::StringRef name, + std::vector packedContents +) { + auto init = llvm::ConstantStruct::getAnon( + packedContents, + true + ); + + auto var = new LLGlobalVariable( + module, + init->getType(), + false, + LLGlobalValue::ExternalLinkage, + init, + name, + nullptr, + + ); + var->setSection("__DATA,objc_data,regular"); + + return var; +} + LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section) { - auto init = llvm::ConstantDataArray::getString(module.getContext(), str); - auto var = new LLGlobalVariable(module, init->getType(), false, - LLGlobalValue::PrivateLinkage, init, symbol); - var->setSection(section); - return var; + auto init = llvm::ConstantDataArray::getString(module.getContext(), str); + auto var = new LLGlobalVariable(module, init->getType(), false, + LLGlobalValue::PrivateLinkage, init, symbol); + var->setSection(section); + return var; } -LLGlobalVariable *ObjCState::getMethVarName(const llvm::StringRef &name) { - auto it = methVarNameMap.find(name); - if (it != methVarNameMap.end()) { +LLGlobalVariable *ObjCState::getMethodVarName(const llvm::StringRef &name) { + auto it = methVarNameMap.find(name); + if (it != methVarNameMap.end()) { return it->second; - } + } - auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, - abi == nonFragile - ? "__TEXT,__objc_methname,cstring_literals" - : "__TEXT,__cstring,cstring_literals"); - methVarNameMap[name] = var; - retain(var); - return var; + auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, + "__TEXT,__objc_methname,cstring_literals"); + methVarNameMap[name] = var; + retain(var); + return var; } -LLGlobalVariable *ObjCState::getMethVarRef(const ObjcSelector &sel) { - llvm::StringRef s(sel.stringvalue, sel.stringlen); - auto it = methVarRefMap.find(s); - if (it != methVarRefMap.end()) { - return it->second; - } +LLGlobalVariable *ObjCState::getMethodVarRef(const ObjcSelector &sel) { + llvm::StringRef s(sel.stringvalue, sel.stringlen); + auto it = methodVarRefTable.find(s); + if (it != methodVarRefTable.end()) { + return it->second; + } + + auto gvar = getMethVarName(s); + auto selref = new LLGlobalVariable( + module, gvar->getType(), + false, // prevent const elimination optimization + LLGlobalValue::PrivateLinkage, gvar, "OBJC_SELECTOR_REFERENCES_", nullptr, + LLGlobalVariable::NotThreadLocal, 0, + true + ); // externally initialized + + selref->setSection("__DATA,__objc_selrefs,literal_pointers,no_dead_strip"); - auto gvar = getMethVarName(s); - auto selref = new LLGlobalVariable( - module, gvar->getType(), - false, // prevent const elimination optimization - LLGlobalValue::PrivateLinkage, gvar, "OBJC_SELECTOR_REFERENCES_", nullptr, - LLGlobalVariable::NotThreadLocal, 0, - true); // externally initialized - selref->setSection( - abi == nonFragile - ? "__DATA,__objc_selrefs,literal_pointers,no_dead_strip" - : "__OBJC,__message_refs,literal_pointers,no_dead_strip"); - - // Save for later lookup and prevent optimizer elimination - methVarRefMap[s] = selref; - retain(selref); - - return selref; + // Save for later lookup and prevent optimizer elimination + methVarRefMap[s] = selref; + retain(selref); + + return selref; } void ObjCState::retain(LLConstant *sym) { @@ -96,36 +139,35 @@ void ObjCState::retain(LLConstant *sym) { } void ObjCState::finalize() { - if (!retainedSymbols.empty()) { - genImageInfo(); - // add in references so optimizer won't remove symbols. - retainSymbols(); - } + if (!retainedSymbols.empty()) { + genImageInfo(); + + // add in references so optimizer won't remove symbols. + retainSymbols(); + } } void ObjCState::genImageInfo() { - // Use LLVM to generate image info - const char *section = - (abi == nonFragile ? "__DATA,__objc_imageinfo,regular,no_dead_strip" - : "__OBJC,__image_info"); - module.addModuleFlag(llvm::Module::Error, "Objective-C Version", - abi); // unused? - module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", - 0u); // version - module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", - llvm::MDString::get(module.getContext(), section)); - module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", - 0u); // flags + // Use LLVM to generate image info + const char *section = "__DATA,__objc_imageinfo,regular,no_dead_strip"; + module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2); // Only support ABI 2. (Non-fragile) + module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", + 0u); // version + module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", + llvm::MDString::get(module.getContext(), section)); + module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", + 0u); // flags } void ObjCState::retainSymbols() { - // put all objc symbols in the llvm.compiler.used array so optimizer won't - // remove. - auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), + + // put all objc symbols in the llvm.compiler.used array so optimizer won't + // remove. + auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), retainedSymbols.size()); - auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); - auto var = new LLGlobalVariable(module, arrayType, false, - LLGlobalValue::AppendingLinkage, usedArray, - "llvm.compiler.used"); - var->setSection("llvm.metadata"); + auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); + auto var = new LLGlobalVariable(module, arrayType, false, + LLGlobalValue::AppendingLinkage, usedArray, + "llvm.compiler.used"); + var->setSection("llvm.metadata"); } diff --git a/gen/objcgen.h b/gen/objcgen.h index a050a091077..3fb8b8f651b 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -24,6 +24,14 @@ class Module; class Triple; } +// Forward decl. +class ClassDeclaration; +class FuncDeclaration; +class InterfaceDeclaration; +class VarDeclaration; + +typedef llvm::StringMap SymbolCache; + bool objc_isSupported(const llvm::Triple &triple); // Objective-C state tied to an LLVM module (object file). @@ -31,22 +39,52 @@ class ObjCState { public: ObjCState(llvm::Module &module) : module(module) {} - llvm::GlobalVariable *getMethVarRef(const ObjcSelector &sel); + // Classes + llvm::GlobalVariable *getClassReference(const ClassDeclaration& cd); + + // Interface variables + llvm::GlobalVariable *getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& bar, bool outputSymbol); + + // Methods + llvm::GlobalVariable *getMethodVarRef(const ObjcSelector &sel); + llvm::GlobalVariable *getMethodVarName(const llvm::StringRef& name); + llvm::GlobalVariable *getMethodVarType(const llvm::StringRef& ty); + llvm::GlobalVariable *getMethodVarType(const FuncDeclaration& ty); + + // Protocols + llvm::GlobalVariable *getProtocolSymbol(const InterfaceDeclaration& id); + void finalize(); private: llvm::Module &module; - // symbols that shouldn't be optimized away + // Symbols that shouldn't be optimized away std::vector retainedSymbols; - llvm::StringMap methVarNameMap; - llvm::StringMap methVarRefMap; + /// Cache for `__OBJC_METACLASS_$_`/`__OBJC_CLASS_$_` symbols. + SymbolCache classNameTable; + SymbolCache classNameRoTable; + + /// Cache for `L_OBJC_CLASSLIST_REFERENCES_$_` symbols. + SymbolCache classReferenceTable; + + /// Cache for `__OBJC_PROTOCOL_$_` symbols. + SymbolCache protocolTable; + + // Cache for methods. + SymbolCache methodVarNameTable; + SymbolCache methodVarRefTable; + SymbolCache methodVarTypeTable; + + // Cache for instance variables. + SymbolCache ivarOffsetTable; llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section); - llvm::GlobalVariable *getMethVarName(const llvm::StringRef &name); + llvm::GlobalVariable *getClassName(const ClassDeclaration& cd, bool isMeta); + void retain(llvm::Constant *sym); void genImageInfo(); From 67ef162875ffa1877b1afb2ef2f0fe28297d8588 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 18 Nov 2024 08:25:35 +0100 Subject: [PATCH 02/67] Further work on implementation --- gen/abi/x86-64.cpp | 4 +- gen/functions.cpp | 4 +- gen/llvmhelpers.cpp | 4 +- gen/objcgen.cpp | 222 +++++++++++++++++++++++++++++++++----------- gen/objcgen.h | 25 ++++- gen/runtime.cpp | 17 +++- gen/tocall.cpp | 46 +++++++-- gen/toir.cpp | 2 +- 8 files changed, 253 insertions(+), 71 deletions(-) diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index 18a26b3c88c..1c3a5f2b98b 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -165,7 +165,7 @@ struct X86_64TargetABI : TargetABI { Type *vaListType() override; - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override; + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override; private: LLType *getValistType(); @@ -393,4 +393,4 @@ const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool supe return "objc_msgSend_fpret"; } return superCall ? "objc_msgSendSuper" : "objc_msgSend"; -} \ No newline at end of file +} diff --git a/gen/functions.cpp b/gen/functions.cpp index 0bde089db0f..ad347937f16 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -149,7 +149,9 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, } } if (hasObjCSelector) { - // TODO: make arg_objcselector to match dmd type + + // SEL is in libobjc an opaque pointer. + // As such a void* is fine. newIrFty.arg_objcSelector = new IrFuncTyArg(Type::tvoidptr, false); ++nextLLArgIdx; } diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 751043480cd..798860003d4 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1843,11 +1843,11 @@ DLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, if (ad->classKind == ClassKind::objc) { auto tHandle = LLType::getInt64Ty(gIR->context()); - auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarOffset(*ad->isClassDeclaration(), *vd, flase)); + auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarOffset(*ad->isClassDeclaration(), *vd, false)); // Offset is now stored in tOffset. LLValue *ptr = src; - ptr = DtoBitCast(ptr, getVoidPtrType()); + ptr = DtoBitCast(ptr, getOpaquePtrType()); ptr = DtoGEP1(llvm::Type::getInt8Ty(gIR->context()), ptr, tOffset); return new DLValue(vd->type, ptr); diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 9342f8f8b60..6636f182740 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -24,7 +24,7 @@ bool objc_isSupported(const llvm::Triple &triple) { // Objective-C only supported on Darwin at this time // Additionally only Objective-C 2 is supported. switch (triple.getArch()) { - case llvm::Triple::aarch64: // arm64 iOS, tvOS + case llvm::Triple::aarch64: // arm64 iOS, tvOS, macOS, watchOS, visionOS case llvm::Triple::arm: // armv6 iOS case llvm::Triple::thumb: // thumbv7 iOS, watchOS case llvm::Triple::x86_64: // OSX, iOS, tvOS sim @@ -38,6 +38,32 @@ bool objc_isSupported(const llvm::Triple &triple) { return false; } +const char *ObjCState::getObjcType(Type *t) { + switch (t->ty) { + case TY::Tvoid: return "v"; + case TY::Tbool: return "B"; + case TY::Tint8: return "c"; + case TY::Tuns8: return "C"; + case TY::Tchar: return "C"; + case TY::Tint16: return "s"; + case TY::Tuns16: return "S"; + case TY::Twchar: return "S"; + case TY::Tint32: return "i"; + case TY::Tuns32: return "I"; + case TY::Tdchar: return "I"; + case TY::Tint64: return "q"; + case TY::Tuns64: return "Q"; + case TY::Tfloat32: return "f"; + case TY::Tcomplex32: return "jf"; + case TY::Tfloat64: return "d"; + case TY::Tcomplex64: return "jd"; + case TY::Tfloat80: return "D"; + case TY::Tcomplex80: return "jD"; + case TY::Tclass: return "@"; + default: return "?"; // unknown + } +} + llvm::GlobalVariable *getGlobal( llvm::Module& module, llvm::StringRef& name, @@ -79,10 +105,12 @@ llvm::GlobalVariable *getGlobalWithBytes( init, name, nullptr, - + LLGlobalVariable::NotThreadLocal, + 0, + false ); + var->setSection("__DATA,objc_data,regular"); - return var; } @@ -96,78 +124,166 @@ LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, return var; } +llvm::Constant *ObjCState::constU32(uint32_t value) { + return llvm::ConstantInt::get( + llvm::Type::getInt32Ty(module.getContext()), + value + ); +} + +llvm::Constant *ObjCState::constU64(uint64_t value) { + return llvm::ConstantInt::get( + llvm::Type::getInt64Ty(module.getContext()), + value + ); +} + +llvm::Constant *ObjCState::constSizeT(size_t value) { + return llvm::ConstantInt::get( + llvm::Type::getIntNTy(module.getContext(), module.getDataLayout().getPointerSizeInBits()), + value + ); +} + +// +// CLASSES +// +LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool meta) { + +} + +LLGlobalVariable *ObjCState::getClassSymbol(const ClassDeclaration& cd, bool meta) { + +} + +LLGlobalVariable *ObjCState::getClassRoSymbol(const ClassDeclaration& cd, bool meta) { + +} + +LLGlobalVariable *ObjCState::getClassReference(const ClassDeclaration& cd) { + +} + + + +// +// PROTOCOLS +// + +LLGlobalVariable *ObjCState::getProtocoList(const InterfaceDeclaration& iface) { + +} + +LLGlobalVariable *ObjCState::getProtocolSymbol(const InterfaceDeclaration& iface) { + + llvm::StringRef name(iface.ident->toChars()); + auto it = protocolTable.find(name); + if (it != protocolTable.end()) { + return it->second; + } + + std::vector members; +} + +LLGlobalVariable *ObjCState::getProtocolReference(const InterfaceDeclaration& iface) { + +} + + + +// +// METHODS +// + +LLGlobalVariable *ObjCState::getMethodVarType(const llvm::StringRef& ty) { + auto it = methodVarTypeTable.find(ty); + if (it != methodVarTypeTable.end()) { + return it->second; + } + + auto var = getCStringVar("OBJC_METH_VAR_TYPE_", ty, + "__TEXT,__objc_methtype,cstring_literals"); + methodVarTypeTable[ty] = var; + retain(var); + return var; +} + LLGlobalVariable *ObjCState::getMethodVarName(const llvm::StringRef &name) { - auto it = methVarNameMap.find(name); - if (it != methVarNameMap.end()) { + auto it = methodVarNameTable.find(name); + if (it != methodVarNameTable.end()) { return it->second; - } + } - auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, - "__TEXT,__objc_methname,cstring_literals"); - methVarNameMap[name] = var; - retain(var); - return var; + auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, + "__TEXT,__objc_methname,cstring_literals"); + methodVarNameTable[name] = var; + retain(var); + return var; } LLGlobalVariable *ObjCState::getMethodVarRef(const ObjcSelector &sel) { - llvm::StringRef s(sel.stringvalue, sel.stringlen); - auto it = methodVarRefTable.find(s); - if (it != methodVarRefTable.end()) { - return it->second; - } + llvm::StringRef s(sel.stringvalue, sel.stringlen); + auto it = methodVarRefTable.find(s); + if (it != methodVarRefTable.end()) { + return it->second; + } - auto gvar = getMethVarName(s); - auto selref = new LLGlobalVariable( - module, gvar->getType(), - false, // prevent const elimination optimization - LLGlobalValue::PrivateLinkage, gvar, "OBJC_SELECTOR_REFERENCES_", nullptr, - LLGlobalVariable::NotThreadLocal, 0, - true - ); // externally initialized - - selref->setSection("__DATA,__objc_selrefs,literal_pointers,no_dead_strip"); + auto gvar = getMethodVarName(s); + auto selref = new LLGlobalVariable( + module, gvar->getType(), + false, // prevent const elimination optimization + LLGlobalValue::PrivateLinkage, gvar, "OBJC_SELECTOR_REFERENCES_", nullptr, + LLGlobalVariable::NotThreadLocal, 0, + true + ); // externally initialized + + selref->setSection("__DATA,__objc_selrefs,literal_pointers,no_dead_strip"); - // Save for later lookup and prevent optimizer elimination - methVarRefMap[s] = selref; - retain(selref); + // Save for later lookup and prevent optimizer elimination + methodVarRefTable[s] = selref; + retain(selref); - return selref; + return selref; } +// +// FINALIZATION +// + void ObjCState::retain(LLConstant *sym) { retainedSymbols.push_back(sym); } void ObjCState::finalize() { - if (!retainedSymbols.empty()) { - genImageInfo(); + if (!retainedSymbols.empty()) { + genImageInfo(); - // add in references so optimizer won't remove symbols. - retainSymbols(); - } + // add in references so optimizer won't remove symbols. + retainSymbols(); + } } void ObjCState::genImageInfo() { - // Use LLVM to generate image info - const char *section = "__DATA,__objc_imageinfo,regular,no_dead_strip"; - module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2); // Only support ABI 2. (Non-fragile) - module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", - 0u); // version - module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", - llvm::MDString::get(module.getContext(), section)); - module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", - 0u); // flags + // Use LLVM to generate image info + const char *section = "__DATA,__objc_imageinfo,regular,no_dead_strip"; + module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2); // Only support ABI 2. (Non-fragile) + module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", + 0u); // version + module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", + llvm::MDString::get(module.getContext(), section)); + module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", + 0u); // flags } void ObjCState::retainSymbols() { - // put all objc symbols in the llvm.compiler.used array so optimizer won't - // remove. - auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), - retainedSymbols.size()); - auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); - auto var = new LLGlobalVariable(module, arrayType, false, - LLGlobalValue::AppendingLinkage, usedArray, - "llvm.compiler.used"); - var->setSection("llvm.metadata"); + // put all objc symbols in the llvm.compiler.used array so optimizer won't + // remove. + auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), + retainedSymbols.size()); + auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); + auto var = new LLGlobalVariable(module, arrayType, false, + LLGlobalValue::AppendingLinkage, usedArray, + "llvm.compiler.used"); + var->setSection("llvm.metadata"); } diff --git a/gen/objcgen.h b/gen/objcgen.h index 3fb8b8f651b..a6bb3aaf79d 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -15,6 +15,7 @@ #include #include "llvm/ADT/StringMap.h" +#include "ir/irfunction.h" struct ObjcSelector; namespace llvm { @@ -40,6 +41,8 @@ class ObjCState { ObjCState(llvm::Module &module) : module(module) {} // Classes + llvm::GlobalVariable *getClassSymbol(const ClassDeclaration& cd, bool meta); + llvm::GlobalVariable *getClassRoSymbol(const ClassDeclaration& cd, bool meta); llvm::GlobalVariable *getClassReference(const ClassDeclaration& cd); // Interface variables @@ -53,6 +56,7 @@ class ObjCState { // Protocols llvm::GlobalVariable *getProtocolSymbol(const InterfaceDeclaration& id); + llvm::GlobalVariable *getProtocolReference(const InterfaceDeclaration& id); void finalize(); @@ -62,10 +66,19 @@ class ObjCState { // Symbols that shouldn't be optimized away std::vector retainedSymbols; + // Store the classes and protocols. + std::vector classes; + std::vector protocols; + /// Cache for `__OBJC_METACLASS_$_`/`__OBJC_CLASS_$_` symbols. SymbolCache classNameTable; SymbolCache classNameRoTable; + /// Cache for `_OBJC_CLASS_$_` symbols stored in `__objc_stubs`. + /// NOTE: Stub classes have a different layout from normal classes + /// And need to be instantiated with a call to the objective-c runtime. + SymbolCache stubClassNameTable; + /// Cache for `L_OBJC_CLASSLIST_REFERENCES_$_` symbols. SymbolCache classReferenceTable; @@ -80,10 +93,20 @@ class ObjCState { // Cache for instance variables. SymbolCache ivarOffsetTable; + // Gets Objective-C type tag + const char *getObjcType(Type *t); + + llvm::Constant *constU32(uint32_t value); + llvm::Constant *constU64(uint64_t value); + llvm::Constant *constSizeT(size_t value); + + llvm::GlobalVariable *getProtocoList(const InterfaceDeclaration& iface); + llvm::GlobalVariable *getProtocoList(const ClassDeclaration& cd); + llvm::GlobalVariable *getMethodListFor(const ClassDeclaration& cd, bool meta); + llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section); - llvm::GlobalVariable *getClassName(const ClassDeclaration& cd, bool isMeta); void retain(llvm::Constant *sym); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 06b0ecab241..f1d6f9439a2 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -824,6 +824,10 @@ static void buildRuntimeModule() { {stringTy, arrayOf(sizeTy), arrayOf(uintTy), ubyteTy}); } + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + if (target.objc.supported) { assert(global.params.targetTriple->isOSDarwin()); @@ -839,6 +843,12 @@ static void buildRuntimeModule() { {objectPtrTy, selectorPtrTy}, {}, AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + // id objc_msgSendSuper(obj_super_t *super, SEL op, ...) + // NOTE: obj_super_t is defined as struct { id, Class } + createFwdDecl(LINK::c, objectPtrTy, {"objc_msgSendSuper"}, + {objectPtrTy, selectorPtrTy}, {}, + AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + switch (global.params.targetTriple->getArch()) { case llvm::Triple::x86_64: // creal objc_msgSend_fp2ret(id self, SEL op, ...) @@ -856,7 +866,12 @@ static void buildRuntimeModule() { // used when return value is aggregate via a hidden sret arg // void objc_msgSend_stret(T *sret_arg, id self, SEL op, ...) createFwdDecl(LINK::c, voidTy, {"objc_msgSend_stret"}, - {objectPtrTy, selectorPtrTy}); + {objectPtrTy, objectPtrTy, selectorPtrTy}); + + // See: https://github.com/apple-oss-distributions/objc4/blob/main/runtime/Messengers.subproj/objc-msg-x86_64.s#L1059 + // void objc_msgSend_stret(T *sret_arg, objc_super_t *super, SEL op, ...) + createFwdDecl(LINK::c, voidTy, {"objc_msgSendSuper_stret"}, + {objectPtrTy, objectPtrTy, selectorPtrTy}); break; default: break; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 97f5ca9f0cd..973d8ac9c2a 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -646,13 +646,13 @@ class ImplicitArgumentsBuilder { tf(DtoTypeFunction(fnval)), llArgTypesBegin(llCalleeType->param_begin()) {} - void addImplicitArgs() { + void addImplicitArgs(bool directcall) { if (gABI->passThisBeforeSret(tf)) { - addContext(); + addContext(directcall); addSret(); } else { addSret(); - addContext(); + addContext(directcall); } addArguments(); @@ -702,11 +702,12 @@ class ImplicitArgumentsBuilder { } // Adds an optional context/this pointer argument and sets hasContext. - void addContext() { + void addContext(bool directcall) { const bool thiscall = irFty.arg_this; const bool nestedcall = irFty.arg_nest; + const bool objccall = irFty.type->linkage == LINK::objc; - hasContext = thiscall || nestedcall || isDelegateCall; + hasContext = thiscall || nestedcall || isDelegateCall || objccall; if (!hasContext) return; @@ -732,8 +733,32 @@ class ImplicitArgumentsBuilder { } args.push_back(thisptrLval); } else if (thiscall && dfnval && dfnval->vthis) { - // ... or a normal 'this' argument - args.push_back(dfnval->vthis); + + if (objccall && directcall) { + + // ... or a Objective-c super call argument + if (auto parentfd = dfnval->func->parent->isFuncDeclaration()) { + if (auto cls = parentfd->parent->isClassDeclaration()) { + + // Create obj_super struct with (this, ) + auto obj_super = DtoAggrPair( + dfnval->vthis, + DtoLoad(getOpaquePtrType(), gIR->objc.getClassReference(*cls)), + "super" + ); + + // Allocate and store obj_super struct into a new variable. + auto clsaddr = DtoRawAlloca(obj_super->getType(), 16, "super"); + DtoStore(obj_super, clsaddr); + + args.push_back(clsaddr); + } + } + } else { + + // ... or a normal 'this' argument + args.push_back(dfnval->vthis); + } } else if (isDelegateCall) { // ... or a delegate context arg LLValue *ctxarg; @@ -767,9 +792,10 @@ class ImplicitArgumentsBuilder { if (irFty.arg_objcSelector) { assert(dfnval); + const auto selector = dfnval->func->objc.selector; assert(selector); - LLGlobalVariable *selptr = gIR->objc.getMethVarRef(*selector); + LLGlobalVariable *selptr = gIR->objc.getMethodVarRef(*selector); args.push_back(DtoLoad(selptr->getValueType(), selptr)); } } @@ -813,7 +839,7 @@ static LLValue *DtoCallableValue(DValue *fn) { // FIXME: this function is a mess ! DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, - Expressions *arguments, LLValue *sretPointer) { + Expressions *arguments, LLValue *sretPointer, bool directcall) { IF_LOG Logger::println("DtoCallFunction()"); LOG_SCOPE @@ -858,7 +884,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, // handle implicit arguments (sret, context/this, _arguments) ImplicitArgumentsBuilder iab(args, attrs, loc, fnval, callableTy, arguments, resulttype, sretPointer); - iab.addImplicitArgs(); + iab.addImplicitArgs(directcall); // handle explicit arguments diff --git a/gen/toir.cpp b/gen/toir.cpp index be09d2a682b..6876c7457fc 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -801,7 +801,7 @@ class ToElemVisitor : public Visitor { } DValue *result = - DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer); + DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer, e->directcall); if (canEmitVTableUnchangedAssumption && dfnval->vtable) { // Reload vtable ptr. It's the first element so instead of GEP+load we can From ffbf6cad15ae19383f412c073b1bcc7064e59fac Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 19 Nov 2024 00:25:42 +0100 Subject: [PATCH 03/67] ObjC dynamic cast --- gen/classes.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++----- gen/classes.h | 2 ++ gen/runtime.cpp | 20 +++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index 5d686f084df..c13c511f0e2 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -327,6 +327,14 @@ DValue *DtoCastClass(const Loc &loc, DValue *val, Type *_to) { return DtoDynamicCastObject(loc, val, _to); } +bool DtoIsObjcLinkage(Type *_to) { + TypeClass *to = static_cast(_to->toBasetype()); + auto sym = to->sym; + + DtoResolveClass(sym); + return sym->classKind == ClassKind::objc; +} + //////////////////////////////////////////////////////////////////////////////// static void resolveObjectAndClassInfoClasses() { @@ -339,15 +347,33 @@ static void resolveObjectAndClassInfoClasses() { } DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { + + resolveObjectAndClassInfoClasses(); + + // Dynamic casting in Objective-C works differently from D. + // We call objc_opt_isKindOfClass to get a bool defining + // whether the cast is valid, if it is then we go ahead. + if (DtoIsObjcLinkage(_to)) { + llvm::Function *kindOfClassFunc = + getRuntimeFunction(loc, gIR->module, "objc_opt_isKindOfClass"); + + // Get the object. + LLValue *obj = DtoRVal(val); + + // objc_opt_isKindOfClass will check if id is null + // by itself, so we don't need to add an extra check. + // objc_opt_isKindOfClass(id) ? id : null + LLValue *objCastable = gIR->CreateCallOrInvoke(kindOfClassFunc, obj); + LLValue *ret = gIR->ir->CreateSelect(objCastable, obj, getNullPtr()); + return new DImValue(_to, ret); + } + // call: // Object _d_dynamic_cast(Object o, ClassInfo c) - llvm::Function *func = getRuntimeFunction(loc, gIR->module, "_d_dynamic_cast"); LLFunctionType *funcTy = func->getFunctionType(); - resolveObjectAndClassInfoClasses(); - // Object o LLValue *obj = DtoRVal(val); assert(funcTy->getParamType(0) == obj->getType()); @@ -368,14 +394,44 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { //////////////////////////////////////////////////////////////////////////////// DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { + + resolveObjectAndClassInfoClasses(); + + // Dynamic casting in Objective-C works differently from D. + // In this case we want to call the Objective-C runtime to first + // get a Class object from the `id`. + // Then check if class_conformsToProtocol returns true, + // if it does, then we can cast and return the casted value, + // otherwise return null. + if (DtoIsObjcLinkage(_to)) { + llvm::Function *getClassFunc = + getRuntimeFunction(loc, gIR->module, "object_getClass"); + + llvm::Function *kindOfProtocolFunc = + getRuntimeFunction(loc, gIR->module, "class_conformsToProtocol"); + + // id -> Class + LLValue *obj = DtoRVal(val); + LLValue *objClass = gIR->CreateCallOrInvoke(getClassFunc, obj); + + // Class && kindOfProtocolFunc(Class) ? id : null + LLValue *ret = gIR->ir->CreateSelect( + gIR->ir->CreateIsNotNull(objClass), + gIR->ir->CreateSelect( + gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass), + obj, + getNullPtr() + ), + getNullPtr() + ); + return new DImValue(_to, ret); + } + // call: // Object _d_interface_cast(void* p, ClassInfo c) - llvm::Function *func = getRuntimeFunction(loc, gIR->module, "_d_interface_cast"); - resolveObjectAndClassInfoClasses(); - // void* p LLValue *ptr = DtoRVal(val); diff --git a/gen/classes.h b/gen/classes.h index 766844fe7c3..28dcd765d9c 100644 --- a/gen/classes.h +++ b/gen/classes.h @@ -39,6 +39,8 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *to); DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *to); +bool DtoIsObjcLinkage(Type *to); + /// Returns pair of function pointer and vtable pointer. std::pair DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index f1d6f9439a2..3240b13bed3 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -849,6 +849,26 @@ static void buildRuntimeModule() { {objectPtrTy, selectorPtrTy}, {}, AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + // Class object_getClass(id obj) + createFwdDecl(LINK::c, objectPtrTy, {"object_getClass"}, + {objectPtrTy}, {}, + AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + + // Needed for safe casting + + // bool objc_opt_isKindOfClass(id obj, Class otherClass) + // This features a fast path over using the msgSend version. + // https://github.com/apple-oss-distributions/objc4/blob/main/runtime/NSObject.mm#L2123 + createFwdDecl(LINK::c, boolTy, {"objc_opt_isKindOfClass"}, + {objectPtrTy, objectPtrTy}, {}, + AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + + // class_conformsToProtocol(Class cls, Protocol *protocol) + createFwdDecl(LINK::c, boolTy, {"class_conformsToProtocol"}, + {objectPtrTy, objectPtrTy}, {}, + AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + + switch (global.params.targetTriple->getArch()) { case llvm::Triple::x86_64: // creal objc_msgSend_fp2ret(id self, SEL op, ...) From f2ac1d2cefe22948031b1fd23c5a9d65e85e1a74 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 19 Nov 2024 00:27:18 +0100 Subject: [PATCH 04/67] Add swift stub class attribute --- dmd/frontend.h | 2 + dmd/id.d | 1 + dmd/objc.d | 57 +++++++++++++++++++++++++++ dmd/objc.h | 2 + gen/runtime.cpp | 6 +++ runtime/druntime/src/core/attribute.d | 8 ++++ 6 files changed, 76 insertions(+) diff --git a/dmd/frontend.h b/dmd/frontend.h index 46b0efd8d16..6faa84e6cc4 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -7454,6 +7454,7 @@ class Objc virtual void checkLinkage(FuncDeclaration* fd) = 0; virtual bool isVirtual(const FuncDeclaration* const fd) const = 0; virtual void setAsOptional(FuncDeclaration* functionDeclaration, Scope* sc) const = 0; + virtual void setAsSwiftStub(FuncDeclaration* functionDeclaration, Scope* sc) const = 0; virtual void validateOptional(FuncDeclaration* functionDeclaration) const = 0; virtual ClassDeclaration* getParent(FuncDeclaration* fd, ClassDeclaration* cd) const = 0; virtual void addToClassMethodList(FuncDeclaration* fd, ClassDeclaration* cd) const = 0; @@ -8738,6 +8739,7 @@ struct Id final static Identifier* udaGNUAbiTag; static Identifier* udaSelector; static Identifier* udaOptional; + static Identifier* udaSwiftStub; static Identifier* udaMustUse; static Identifier* udaStandalone; static Identifier* TRUE; diff --git a/dmd/id.d b/dmd/id.d index 2a03ce9314a..3b72e9d7c64 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -626,6 +626,7 @@ immutable Msgtable[] msgtable = { "udaHidden", "_hidden" }, { "udaNoSanitize", "noSanitize" }, { "udaNoSplitStack", "_noSplitStack" }, + { "udaSwiftStub", "swift"}, // IN_LLVM: DCompute specific types and functionss { "dcompute" }, diff --git a/dmd/objc.d b/dmd/objc.d index 2e18303e4dc..331a13574bc 100644 --- a/dmd/objc.d +++ b/dmd/objc.d @@ -156,6 +156,9 @@ extern (C++) struct ObjcClassDeclaration /// `true` if this class is externally defined. bool isExtern = false; + /// `true` if this class is a Swift stub + bool isSwiftStub = false; + /// Name of this class. Identifier identifier; @@ -264,6 +267,20 @@ extern(C++) abstract class Objc */ abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const; + /** + * Marks the given function declaration as a Swift stub class. + * + * A function declaration is considered optional if it's annotated with the + * UDA: `@(core.attribute.optional)`. Only function declarations inside + * interface declarations and with Objective-C linkage can be declared as + * optional. + * + * Params: + * functionDeclaration = the function declaration to be set as optional + * sc = the scope from the semantic phase + */ + abstract void setAsSwiftStub(FuncDeclaration functionDeclaration, Scope* sc) const; + /** * Validates function declarations declared optional. * @@ -452,6 +469,11 @@ static if (!IN_LLVM) // noop } + override void setAsSwiftStub(FuncDeclaration, Scope*) const + { + // noop + } + override void validateOptional(FuncDeclaration) const { // noop @@ -532,6 +554,7 @@ version (IN_LLVM) {} else { cd.classKind = ClassKind.objc; cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0; + this.setAsSwiftStub(cd, cd._scope); } override void setObjc(InterfaceDeclaration id) @@ -609,6 +632,40 @@ version (IN_LLVM) {} else return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_); } + override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const + { + const count = declaredAsSwiftStubCount(cd, sc); + cd.objc.isSwiftStub = count > 0; + + if (count > 1) + .error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars); + } + + /// Returns: the number of times `cd` has been declared as optional. + private int declaredAsSwiftStubCount(ClassDeclaration cd , Scope* sc) const + { + int count; + + foreachUda(cd, sc, (e) { + if (!e.isTypeExp()) + return 0; + + auto typeExp = e.isTypeExp(); + + if (typeExp.type.ty != Tenum) + return 0; + + auto typeEnum = cast(TypeEnum) typeExp.type; + + if (isCoreUda(typeEnum.sym, Id.udaSwiftStub)) + count++; + + return 0; + }); + + return count; + } + override void setAsOptional(FuncDeclaration fd, Scope* sc) const { const count = declaredAsOptionalCount(fd, sc); diff --git a/dmd/objc.h b/dmd/objc.h index 0390115aeb1..8b5f723129f 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -37,6 +37,7 @@ struct ObjcClassDeclaration { d_bool isMeta; d_bool isExtern; + d_bool isSwiftStub; Identifier* identifier; ClassDeclaration* classDeclaration; @@ -67,6 +68,7 @@ class Objc virtual void checkLinkage(FuncDeclaration* fd) = 0; virtual bool isVirtual(const FuncDeclaration*) const = 0; virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0; + virtual void setAsSwiftStub(FuncDeclaration *fd, Scope *sc) const = 0; virtual void validateOptional(FuncDeclaration *fd) const = 0; virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0; virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0; diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 3240b13bed3..9e00ee4c1b2 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -854,6 +854,12 @@ static void buildRuntimeModule() { {objectPtrTy}, {}, AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + // Class objc_loadClassRef(Class function(Class* stub)) + // SEE: https://github.com/swiftlang/swift/blob/main/docs/ObjCInterop.md + createFwdDecl(LINK::c, objectPtrTy, {"objc_loadClassRef"}, + {objectPtrTy}, {}, + AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); + // Needed for safe casting // bool objc_opt_isKindOfClass(id obj, Class otherClass) diff --git a/runtime/druntime/src/core/attribute.d b/runtime/druntime/src/core/attribute.d index 95a67ea219d..bd5f5d474ed 100644 --- a/runtime/druntime/src/core/attribute.d +++ b/runtime/druntime/src/core/attribute.d @@ -317,3 +317,11 @@ enum mustuse; * This is only allowed on `shared` static constructors, not thread-local module constructors. */ enum standalone; + +/** + * Use this attribute to indicate that a Objective-C class is a Swift stub class. + * + * This is only allowed on classes, and classes marked as swift Objective-C classes + * cannot be subclassed. + */ +enum swift; \ No newline at end of file From cb785fb75c5a9b4ffeaede60f8fbba4ef017b4ae Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 19 Nov 2024 10:06:42 +0100 Subject: [PATCH 05/67] Classes, protocols and ivars --- gen/classes.cpp | 4 +- gen/llvmhelpers.h | 2 +- gen/objcgen.cpp | 682 +++++++++++++++++++++++++++++++++++++++------- gen/objcgen.h | 118 ++++++-- gen/tocall.cpp | 2 +- gen/tollvm.cpp | 6 + gen/tollvm.h | 3 + 7 files changed, 692 insertions(+), 125 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index c13c511f0e2..7895e0c9e62 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -416,7 +416,9 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // Class && kindOfProtocolFunc(Class) ? id : null LLValue *ret = gIR->ir->CreateSelect( - gIR->ir->CreateIsNotNull(objClass), + gIR->ir->CreateIsNotNull( + gIR->objc.unmaskPointer(objClass) // Classes may have metadata in their pointer, so remove it. + ), gIR->ir->CreateSelect( gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass), obj, diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index fdf1f5373c9..23ad6b9e596 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -205,7 +205,7 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, /// DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, - Expressions *arguments, LLValue *sretPointer = nullptr); + Expressions *arguments, LLValue *sretPointer = nullptr, bool directcall = false); Type *stripModifiers(Type *type, bool transitive = false); diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 6636f182740..a1ced2acdd0 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -13,10 +13,12 @@ #include "dmd/mtype.h" #include "dmd/objc.h" +#include "dmd/expression.h" +#include "dmd/declaration.h" +#include "dmd/identifier.h" #include "gen/irstate.h" - -#define OBJC_CLASS_LIST = "__DATA,__objc_classlist"; -#define OBJC_PROTOCOL_LIST = "__DATA,__objc_protolist"; +#include "gen/tollvm.h" +#include "ir/irfunction.h" bool objc_isSupported(const llvm::Triple &triple) { if (triple.isOSDarwin()) { @@ -38,8 +40,101 @@ bool objc_isSupported(const llvm::Triple &triple) { return false; } -const char *ObjCState::getObjcType(Type *t) { +llvm::GlobalVariable *ObjCState::getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type = nullptr) { + if (type == nullptr) + type = getOpaquePtrType(); + + auto var = new LLGlobalVariable( + module, + type, + false, + LLGlobalValue::ExternalLinkage, + nullptr, + name, + nullptr, + LLGlobalVariable::NotThreadLocal, + 0, + true + ); + return var; +} + +llvm::GlobalVariable *ObjCState::getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents) { + auto init = llvm::ConstantStruct::getAnon( + packedContents, + true + ); + + auto var = new LLGlobalVariable( + module, + init->getType(), + false, + LLGlobalValue::ExternalLinkage, + init, + name, + nullptr, + LLGlobalVariable::NotThreadLocal, + 0, + false + ); + + var->setSection(dataSection); + return var; +} + +LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, + const llvm::StringRef &str, + const char *section) { + auto init = llvm::ConstantDataArray::getString(module.getContext(), str); + auto var = new LLGlobalVariable(module, init->getType(), false, + LLGlobalValue::PrivateLinkage, init, symbol); + var->setSection(section); + return var; +} + +std::string ObjCState::getObjcTypeEncoding(Type *t) { + std::string tmp; + switch (t->ty) { + case TY::Tpointer: + + // C string (char*) + if (t->nextOf()->ty == TY::Tchar) + return "*"; + + tmp.append("^"); + tmp.append(getObjcTypeEncoding(t->nextOf())); + return tmp; + case TY::Tsarray: + + // Static arrays are encoded in the form of: + // [] + auto typ = t->isTypeSArray(); + uinteger_t count = typ->dim->toUInteger(); + tmp.append("["); + tmp.append(std::to_string(count)); + tmp.append(getObjcTypeEncoding(typ->next)); + tmp.append("]"); + return tmp; + case TY::Tstruct: + + // Structs are encoded in the form of: + // {=} + // Unions are encoded as + // (=) + auto sym = t->isTypeStruct()->sym; + bool isUnion = sym->isUnionDeclaration(); + + tmp.append(isUnion ? "(" : "{"); + tmp.append(t->toChars()); + tmp.append("="); + + for(unsigned int i = 0; i < sym->numArgTypes(); i++) { + tmp.append(getObjcTypeEncoding(sym->argType(i))); + } + + tmp.append(isUnion ? ")" : "}"); + return tmp; case TY::Tvoid: return "v"; case TY::Tbool: return "B"; case TY::Tint8: return "c"; @@ -59,176 +154,527 @@ const char *ObjCState::getObjcType(Type *t) { case TY::Tcomplex64: return "jd"; case TY::Tfloat80: return "D"; case TY::Tcomplex80: return "jD"; - case TY::Tclass: return "@"; + case TY::Tclass: + if (auto klass = t->isTypeClass()) { + return klass->sym->classKind == ClassKind::objc ? "@" : "?"; + } + return "?"; default: return "?"; // unknown } } -llvm::GlobalVariable *getGlobal( - llvm::Module& module, - llvm::StringRef& name, - llvm::Type* type = nullptr -) { - if (type == nullptr) - type = llvm::PointerType::get(llvm::Type::getVoidTy(module.getContext()), 0); - - auto var = new LLGlobalVariable( - module, - type, - false, - LLGlobalValue::ExternalLinkage, - nullptr, - name, - nullptr, - LLGlobalVariable::NotThreadLocal, - 0, - true - ); - return var; +// Helper functions to generate name symbols + +// +// STRING HELPERS +// + +std::string ObjCState::getObjcClassRoSymbol(const ClassDeclaration& cd, bool meta) { + return getObjcSymbolName(meta ? "OBJC_METACLASS_RO_$_" : "OBJC_CLASS_RO_$_", cd.toChars()); } -llvm::GlobalVariable *getGlobalWithBytes( - llvm::Module& module, - llvm::StringRef name, - std::vector packedContents -) { - auto init = llvm::ConstantStruct::getAnon( - packedContents, - true - ); - - auto var = new LLGlobalVariable( - module, - init->getType(), - false, - LLGlobalValue::ExternalLinkage, - init, - name, - nullptr, - LLGlobalVariable::NotThreadLocal, - 0, - false - ); - - var->setSection("__DATA,objc_data,regular"); - return var; +std::string ObjCState::getObjcClassSymbol(const ClassDeclaration& cd, bool meta) { + return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", cd.toChars()); } -LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, - const llvm::StringRef &str, - const char *section) { - auto init = llvm::ConstantDataArray::getString(module.getContext(), str); - auto var = new LLGlobalVariable(module, init->getType(), false, - LLGlobalValue::PrivateLinkage, init, symbol); - var->setSection(section); - return var; +std::string ObjCState::getObjcMethodListSymbol(const ClassDeclaration& cd, bool meta) { + return getObjcSymbolName(meta ? "OBJC_$_CLASS_METHODS_" : "OBJC_$_INSTANCE_METHODS_", cd.objc.identifier->toChars()); } -llvm::Constant *ObjCState::constU32(uint32_t value) { - return llvm::ConstantInt::get( - llvm::Type::getInt32Ty(module.getContext()), - value - ); +std::string ObjCState::getObjcProtoSymbol(const InterfaceDeclaration& iface) { + return getObjcSymbolName("OBJC_PROTOCOL_$_", iface.toChars()); } -llvm::Constant *ObjCState::constU64(uint64_t value) { - return llvm::ConstantInt::get( - llvm::Type::getInt64Ty(module.getContext()), - value - ); +std::string ObjCState::getObjcIvarSymbol(const ClassDeclaration& cd, const VarDeclaration& var) { + return std::string("OBJC_IVAR_$_", cd.objc.identifier->toChars(), ".", var.iden->toChars()); } -llvm::Constant *ObjCState::constSizeT(size_t value) { - return llvm::ConstantInt::get( - llvm::Type::getIntNTy(module.getContext(), module.getDataLayout().getPointerSizeInBits()), - value - ); +std::string ObjCState::getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { + return std::string(dsymPrefix, dsymName); +} + +// +// UTILITIES +// + +LLGlobalVariable *ObjCState::getTypeEncoding(const Declaration& decl) { + std::string out_ = getObjcTypeEncoding(decl.type->nextOf()); + + if (decl.parameters) { + for (size_t i = 0; i < decl.parameters->length; i++) + out_.append(getObjcTypeEncoding(decl.parameters[i]->type)); + } + + return getMethodVarType(out_); +} + +llvm::GlobalVariable* ObjCState::getEmptyCache() { + static llvm::GlobalVariable* g; + if(g == nullptr) + g = getGlobal(module, "_objc_empty_cache"); + return g; +} + +llvm::Value *ObjCState::unmaskPointer(llvm::Value *value) { + return gIR->ir->CreateAnd(value, OBJC_PTRMASK); } // // CLASSES // -LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool meta) { +unsigned int getClassFlags(const ClassDeclaration& cd) { + unsigned int flags = 0; + if (cd.objc.isRootClass()) + flags |= RO_ROOT; + + if (cd.objc.isMeta) + flags |= RO_META; + + return flags; } LLGlobalVariable *ObjCState::getClassSymbol(const ClassDeclaration& cd, bool meta) { + llvm::StringRef name(cd.toChars()); + auto it = classSymbolTable.find(name); + if (it != classSymbolTable.end()) { + return it->second; + } + + // Extern objects + if (cd.objc.isExtern) { + auto var = getGlobal(module, name); + + classSymbolTable[name] = var; + retain(var); + return var; + } + + // Classes and Metaclasses are the same thing in Objective-C + // + // Class symbol layout is as follows: + // struct objc_class { + // Class isa; // inherited from objc_object + // Class superclass; + // cache_t cache; // Formerly vtable and cache pointer + // class_data_bits_t bits; + // } + // + // the isa pointer points to the metaclass of the class. + // If the class is a metaclass, it points to the *root* metaclass. + // + // The superclass pointer will always will always point to the superclass + // of either the class or meta class. + ConstantList members; + if (meta) { + + // Find root meta-class. + const ClassDeclaration *metaDecl = &cd; + while(metaDecl->baseClass) + metaDecl = metaDecl->baseClass; + + members.push_back(getClassSymbol(*metaDecl, true)); + } else { + + // Both register class and push metaclass on as the isa pointer. + classes.push_back(const_cast(&cd)); + members.push_back(getClassSymbol(cd, true)); + } + + // Set the superclass field. + members.push_back( + wrapNull(cd.baseClass ? getClassSymbol(*cd.baseClass, meta) : nullptr) + ); + + // cache_t and class_data_bits_t + members.push_back(getEmptyCache()); + members.push_back(getNullPtr()); + + // Attach Class read-only struct ref. + members.push_back( + wrapNull(getClassRoSymbol(cd, meta)) + ); + + // Cache it. + auto var = getGlobalWithBytes(module, name, members); + classSymbolTable[name] = var; + retain(var); + return var; +} + +LLGlobalVariable *ObjCState::getClassRoName(const ClassDeclaration& cd) { + llvm::StringRef name(cd.toChars()); + auto it = classRoNameTable.find(name); + if (it != classRoNameTable.end()) { + return it->second; + } + auto var = getCStringVar("OBJC_CLASS_NAME", name, classNameSection); + classRoNameTable[name] = var; + retain(var); + return var; } LLGlobalVariable *ObjCState::getClassRoSymbol(const ClassDeclaration& cd, bool meta) { + auto name = getObjcClassRoSymbol(cd, meta); + auto it = classRoSymbolTable.find(name); + if (it != classRoSymbolTable.end()) { + return it->second; + } + + // Class read-only table layout is as follows: + // struct class_ro_t { + // uint32_t flags; + // uint32_t instanceStart; + // uint32_t instanceSize; + // uint32_t reserved; // Only on 64 bit platforms! + // const uint8_t * ivarLayout; + // const char * name; + // method_list_t * baseMethodList; + // protocol_list_t * baseProtocols; + // const ivar_list_t * ivars; + // const uint8_t * weakIvarLayout; + // property_list_t *baseProperties; + // }; + std::vector members; + members.push_back(DtoConstUint(getClassFlags(cd))); // flags + members.push_back(DtoConstUint(0)); // instanceStart + members.push_back(DtoConstUint(0)); // instanceSize + if (getPointerSizeInBits() == 64) + members.push_back(DtoConstUint(0)); // reserved + members.push_back(getNullPtr()); // ivarLayout + members.push_back(getClassRoName(cd)); // name + members.push_back(wrapNull(getMethodListFor(cd, meta, false))); // baseMethodList + members.push_back(wrapNull(getProtocolListFor(cd))); // baseProtocols + + if (meta) { + members.push_back(DtoConstUint(0)); // ivars + members.push_back(DtoConstUint(0)); // weakIvarLayout + members.push_back(DtoConstUint(0)); // baseProperties + } else { + auto ivarList = getIVarListFor(cd); + + members.push_back(ivarList); // ivars + members.push_back(DtoConstUint(0)); // weakIvarLayout + + // TODO: Implement Objective-C properties. + members.push_back(DtoConstUint(0)); // baseProperties + } + + auto var = getGlobalWithBytes(module, name, members); + var->setSection(constSection); + classRoSymbolTable[name] = var; + retain(var); + return var; } LLGlobalVariable *ObjCState::getClassReference(const ClassDeclaration& cd) { + llvm::StringRef name(cd.objc.identifier->toChars()); + auto it = classReferenceTable.find(name); + if (it != classReferenceTable.end()) { + return it->second; + } + auto gvar = getClassSymbol(cd); + auto classref = new LLGlobalVariable( + module, gvar->getType(), + false, + LLGlobalValue::PrivateLinkage, gvar, "OBJC_CLASSLIST_REFERENCES_$_", nullptr, + LLGlobalVariable::NotThreadLocal, 0, + true + ); // externally initialized + + classref->setSection(classRefsSection); + + // Save for later lookup and prevent optimizer elimination + classReferenceTable[name] = classref; + retain(classref); + return classref; } +// +// CATEGORIES +// + // // PROTOCOLS // -LLGlobalVariable *ObjCState::getProtocoList(const InterfaceDeclaration& iface) { +LLGlobalVariable *ObjCState::getMethodName(const llvm::StringRef &name) { + auto it = methodNameTable.find(name); + if (it != methodNameTable.end()) { + return it->second; + } + + auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, methodNameSection); + methodNameTable[name] = var; + retain(var); + return var; +} + +LLGlobalVariable *ObjCState::getProtocolListFor(const ClassDeclaration& cd) { + llvm::StringRef name(cd.objc.identifier->toChars()); + auto it = protocolListTable.find(name); + if (it != protocolListTable.end()) { + return it->second; + } + + // Protocol list layout is as follows: + // struct protocol_list_t { + // uintptr_t count; // count is 64-bit by accident. + // protocol_ref_t list[0]; // variable-size + // } + std::vector members; + members.push_back(constU64(cd.interfaces.length)); + for(size_t i; i < cd.interfaces.length; i++) { + auto iface = cd.interfaces[i]->sym->isInterfaceDeclaration(); + members.push_back(getProtocolReference(*iface)); + } + // Cache and return. + auto var = getGlobalWithBytes(module, name, members); + protocolListTable[name] = var; + retain(var); + return var; } LLGlobalVariable *ObjCState::getProtocolSymbol(const InterfaceDeclaration& iface) { - - llvm::StringRef name(iface.ident->toChars()); + llvm::StringRef name(iface.objc.identifier->toChars()); auto it = protocolTable.find(name); if (it != protocolTable.end()) { return it->second; } + // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L277 + // + // Protocol symbol layout is as follows: + // struct protocol_t { + // Class isa; // inherited from objc_object + // const char *mangledName; + // protocol_list_t *protocols; // This list is seperate from the module-level protocol list! + // method_list_t *instanceMethods; + // method_list_t *classMethods; + // method_list_t *optionalInstanceMethods; + // method_list_t *optionalClassMethods; + // property_list_t *instanceProperties; + // uint32_t size; // sizeof(protocol_t) + // uint32_t flags; + // } std::vector members; + + size_t protocolTSize = (getPointerSize()*8)+8; + + auto nameConst = llvm::ConstantDataArray::getString(module.getContext(), name); + members.push_back(getNullPtr()); // unused? + members.push_back(nameConst); // mangledName + members.push_back(getProtocolListFor(iface)); // protocols + members.push_back(getMethodListFor(iface, false)); // instanceMethods + members.push_back(getMethodListFor(iface, true)); // classMethods + members.push_back(getNullPtr()); // TODO: optionalInstanceMethods + members.push_back(getNullPtr()); // TODO: optionalClassMethods + members.push_back(getNullPtr()); // TODO: instanceProperties + members.push_back(DtoConstUint(protocolTSize)); + members.push_back(DtoConstUint(0)); // Should always be 0, other values are reserved for runtime. + + auto var = getGlobalWithBytes(module, name, members); + protocolTable[name] = var; + retain(var); } LLGlobalVariable *ObjCState::getProtocolReference(const InterfaceDeclaration& iface) { + llvm::StringRef name(iface.objc.identifier->toChars()); + auto it = protocolReferenceTable.find(name); + if (it != protocolReferenceTable.end()) { + return it->second; + } + auto gvar = getProtocolSymbol(iface); + auto protoref = new LLGlobalVariable( + module, gvar->getType(), + false, // prevent const elimination optimization + LLGlobalValue::PrivateLinkage, gvar, "OBJC_PROTOLIST_REFERENCES_$_", nullptr, + LLGlobalVariable::NotThreadLocal, 0, + true + ); // externally initialized + + protoref->setSection(protoRefsSection); + + // Save for later lookup and prevent optimizer elimination + protocolReferenceTable[name] = protoref; + retain(protoref); + return protoref; } +// +// INSTANCE VARIABLES +// +LLGlobalVariable *ObjCState::getIVarListFor(const ClassDeclaration& cd) { + auto name = getObjcSymbolName("OBJC_$_INSTANCE_VARIABLES_", cd.objc.identifier->toChars()); + auto it = ivarListTable.find(name); + if (it != ivarListTable.end()) { + return it->second; + } + + // If there's no fields, just return a null constant. + if (cd.fields.length > 0) { + + auto var = getNullPtr(); + ivarListTable[name] = var; + return var; + } + + ConstantList members; + members.push_back(DtoConstUint(OBJC_IVAR_ENTSIZE)); + members.push_back(DtoConstUint(cd.fields.length)); + for(size_t i; i < cd.fields.length; i++) { + if (auto vd = cd.fields[i]->isVarDeclaration()) ( + members.push_back(getIVarSymbol(cd, *vd)); + ) + } + + auto var = getGlobalWithBytes(module, globalName, members); + var->setSection(constSection); + ivarListTable[name] = var; + retain(var); + return var; +} + +LLGlobalVariable *ObjCState::getIVarSymbol(const ClassDeclaration& cd, const VarDeclaration& var) { + // struct ivar_t { + // int32_t *offset; + // const char *name; + // const char *type; + // // alignment is sometimes -1; use alignment() instead + // uint32_t alignment_raw; + // uint32_t size; + // } + auto name = getObjcIvarSymbol(cd, var); + auto it = ivarTable.find(name); + if (it != ivarTable.end()) { + return it->second; + } + + ConstantList members; + members.push_back(getIVarOffset(cd, var, false)); + members.push_back(getMethodVarName(var.ident->toChars())); + members.push_back(getTypeEncoding(var.type)); + members.push_back(DtoConstUint(var.alignment.isDefault() ? -1 : var.alignment.get())); + members.push_back(DtoConstUint(var.size(var.loc))); + + auto var = getGlobalWithBytes(module, name, members); + ivarTable[name] = var; + retain(var); + return var; +} + +llvm::GlobalVariable *ObjCState::getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol) { + auto nameBuf = std::string("OBJC_IVAR_$_", cd.objc.identifier->toChars(), ".", var.iden->toChars()); + auto name = llvm::StringRef(nameBuf); + auto it = ivarOffsetTable.find(name); + if (it != ivarOffsetTable.end()) { + return it->second; + } + + LLGlobalVariable *var; + if (cd.objc.isMeta) { + + var = getGlobal(module, name); + ivarOffsetTable[name] = var; + retain(var); + return var; + } + + ConstantList members; + members.push_back(DtoConstUlong(var.offset)); + + var = getGlobalWithBytes(module, name, members); + ivarOffsetTable[name] = var; + retain(var); + return var; +} // // METHODS // -LLGlobalVariable *ObjCState::getMethodVarType(const llvm::StringRef& ty) { - auto it = methodVarTypeTable.find(ty); - if (it != methodVarTypeTable.end()) { +LLGlobalVariable *ObjCState::getMethodVarName(const llvm::StringRef &name) { + auto it = methodNameTable.find(name); + if (it != methodNameTable.end()) { return it->second; } - auto var = getCStringVar("OBJC_METH_VAR_TYPE_", ty, - "__TEXT,__objc_methtype,cstring_literals"); - methodVarTypeTable[ty] = var; + auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, methodNameSection); + methodNameTable[name] = var; retain(var); return var; } -LLGlobalVariable *ObjCState::getMethodVarName(const llvm::StringRef &name) { - auto it = methodVarNameTable.find(name); - if (it != methodVarNameTable.end()) { +LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional=false) { + llvm::StringRef name(cd.objc.identifier->toChars()); + auto it = methodListTable.find(name); + if (it != methodListTable.end()) { + return it->second; + } + + auto methods = meta ? cd.objc.metaclass->objc.methodList : cd.objc.methodList; + + // Count the amount of methods with a body. + size_t methodCount; + for(size_t i; i < methods.length; i++) { + if (methods[i]->fbody) methodCount++; + } + + // Empty classes don't need a method list generated. + if (!methodCount) + return nullptr; + + ConstantList members; + + // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L93 + members.push_back(DtoConstUint(OBJC_METHOD_SIZEOF)); + members.push_back(DtoConstUint(methodCount)); + for(size_t i; i < methods.length; i++) { + if (methods[i]->fbody) { + auto selector = methods[i]->objc.selector; + + // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L207 + llvm::StringRef name(selector->stringvalue, selector->stringlen); + members.push_back(getMethodSymbol(name)); + members.push_back(getMethodType(name)); + members.push_back(DtoCallee(method)); + } + } + + auto var = getGlobalWithBytes(module, getObjcMethodListSymbol(cd, meta), members); + methodListTable[name] = var; + retain(var); + return var; +} + +LLGlobalVariable *ObjCState::getMethodVarType(const llvm::StringRef& name) { + auto it = methodTypeTable.find(name); + if (it != methodTypeTable.end()) { return it->second; } - auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, - "__TEXT,__objc_methname,cstring_literals"); - methodVarNameTable[name] = var; + auto var = getCStringVar("OBJC_METH_VAR_TYPE_", encoding, methodTypeSection); + methodTypeTable[name] = var; retain(var); return var; } -LLGlobalVariable *ObjCState::getMethodVarRef(const ObjcSelector &sel) { - llvm::StringRef s(sel.stringvalue, sel.stringlen); - auto it = methodVarRefTable.find(s); - if (it != methodVarRefTable.end()) { +LLGlobalVariable *ObjCState::getSelector(const ObjcSelector &sel) { + llvm::StringRef name(sel.stringvalue, sel.stringlen); + auto it = selectorTable.find(name); + if (it != selectorTable.end()) { return it->second; } - auto gvar = getMethodVarName(s); + auto gvar = getMethodName(name); auto selref = new LLGlobalVariable( module, gvar->getType(), false, // prevent const elimination optimization @@ -237,12 +683,11 @@ LLGlobalVariable *ObjCState::getMethodVarRef(const ObjcSelector &sel) { true ); // externally initialized - selref->setSection("__DATA,__objc_selrefs,literal_pointers,no_dead_strip"); + selref->setSection(selectorRefsSection); // Save for later lookup and prevent optimizer elimination - methodVarRefTable[s] = selref; + selectorTable[name] = selref; retain(selref); - return selref; } @@ -254,8 +699,46 @@ void ObjCState::retain(LLConstant *sym) { retainedSymbols.push_back(sym); } +llvm::Constant *ObjCState::finalizeClasses() { + + // Objective-C needs to know which classes are in the output + // As such a protocol list needs to be generated. + std::vector members; + for(auto classRef = classes.begin(); classRef != classes.end(); ++classRef) { + auto klass = *classRef; + if (!klass->objc.isExtern && !klass->objc.isMeta) { + members.push_back(getClassSymbol(*klass)); + } + } + + auto var = getGlobalWithBytes(module, "OBJC_CLASS_$_", members); + var->setSection(classListSection); + return var; +} + +llvm::Constant *ObjCState::finalizeProtocols() { + + // Objective-C needs to know which protocols are in the output + // As such a protocol list needs to be generated. + std::vector members; + for(auto protoRef = protocols.begin(); protoRef != protocols.end(); ++protoRef) { + auto proto = *protoRef; + if (!proto->objc.isExtern) { + members.push_back(getProtocolSymbol(*proto)); + } + } + + auto var = getGlobalWithBytes(module, "OBJC_PROTOCOL_$_", members); + var->setSection(protoListSection); + return var; +} + void ObjCState::finalize() { if (!retainedSymbols.empty()) { + + retainedSymbols.push_back(finalizeProtocols()); + retainedSymbols.push_back(finalizeClasses()); + genImageInfo(); // add in references so optimizer won't remove symbols. @@ -265,12 +748,11 @@ void ObjCState::finalize() { void ObjCState::genImageInfo() { // Use LLVM to generate image info - const char *section = "__DATA,__objc_imageinfo,regular,no_dead_strip"; module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2); // Only support ABI 2. (Non-fragile) module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", - llvm::MDString::get(module.getContext(), section)); + llvm::MDString::get(module.getContext(), imageInfoSectionName)); module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags } diff --git a/gen/objcgen.h b/gen/objcgen.h index a6bb3aaf79d..55deec96bd5 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -15,7 +15,6 @@ #include #include "llvm/ADT/StringMap.h" -#include "ir/irfunction.h" struct ObjcSelector; namespace llvm { @@ -30,86 +29,161 @@ class ClassDeclaration; class FuncDeclaration; class InterfaceDeclaration; class VarDeclaration; +class Identifier; typedef llvm::StringMap SymbolCache; +typedef std::vector ConstantList; bool objc_isSupported(const llvm::Triple &triple); +// A mask needed to extract pointers from tagged Objective-C pointers. +// Only lower 35-bits in tagged pointers are used. +#define OBJC_PTRMASK 0x7ffffffff + +// TODO: maybe make this slightly less cursed by actually +// poking the Objective-C library? +#define OBJC_METHOD_SIZEOF 24 +#define OBJC_IVAR_ENTSIZE 32 + +// class is a metaclass +#define RO_META (1<<0) + +// class is a root class +#define RO_ROOT (1<<1) + // Objective-C state tied to an LLVM module (object file). class ObjCState { public: - ObjCState(llvm::Module &module) : module(module) {} + ObjCState(llvm::Module &module) : module(module) { } + + // General + llvm::GlobalVariable *getTypeEncoding(const Declaration& ty); // Classes llvm::GlobalVariable *getClassSymbol(const ClassDeclaration& cd, bool meta); llvm::GlobalVariable *getClassRoSymbol(const ClassDeclaration& cd, bool meta); llvm::GlobalVariable *getClassReference(const ClassDeclaration& cd); + // Categories + // Interface variables - llvm::GlobalVariable *getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& bar, bool outputSymbol); + llvm::GlobalVariable *getIVarListFor(const ClassDeclaration& cd); + llvm::GlobalVariable *getIVarSymbol(const ClassDeclaration& cd, const VarDeclaration& var); + llvm::GlobalVariable *getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol); // Methods - llvm::GlobalVariable *getMethodVarRef(const ObjcSelector &sel); - llvm::GlobalVariable *getMethodVarName(const llvm::StringRef& name); llvm::GlobalVariable *getMethodVarType(const llvm::StringRef& ty); - llvm::GlobalVariable *getMethodVarType(const FuncDeclaration& ty); + + // Selector + llvm::GlobalVariable *getSelector(const ObjcSelector &sel); // Protocols + llvm::GlobalVariable *getProtocolListFor(const ClassDeclaration& cd); llvm::GlobalVariable *getProtocolSymbol(const InterfaceDeclaration& id); llvm::GlobalVariable *getProtocolReference(const InterfaceDeclaration& id); + // Unmasks pointer to make sure it's not a tagged pointer. + llvm::Value *unmaskPointer(llvm::Value *value); + void finalize(); + // Section Names + const char *imageInfoSection = "__DATA,__objc_imageinfo, regular, no_dead_strip"; + + // Lists + const char *classListSection = "__DATA,__objc_classlist, regular, no_dead_strip"; + const char *protoListSection = "__DATA,__objc_protolist, regular, no_dead_strip"; + const char *catListSection = "__DATA,__objc_catlist, regular, no_dead_strip"; + + const char *classNameSection = "__TEXT,__objc_classname, cstring_literals, regular, no_dead_strip"; + const char *classStubsSection = "__DATA,__objc_stubs, regular, no_dead_strip"; + + const char *constSection = "__DATA,__objc_const, regular, no_dead_strip"; + const char *dataSection = "__DATA,__objc_data, regular, no_dead_strip"; + + const char *methodNameSection = "__TEXT,__objc_methname, cstring_literals, regular, no_dead_strip"; + const char *methodTypeSection = "__TEXT,__objc_methtype, regular, no_dead_strip"; + + const char *classRefsSection = "__DATA,__objc_classrefs, regular, no_dead_strip"; + const char *protoRefsSection = "__DATA,__objc_protorefs, regular, no_dead_strip"; + const char *selectorRefsSection = "__DATA,__objc_selrefs, regular, no_dead_strip"; + private: llvm::Module &module; // Symbols that shouldn't be optimized away std::vector retainedSymbols; - // Store the classes and protocols. + // Store the classes, protocols and new selectors. std::vector classes; std::vector protocols; + std::vector selectors; /// Cache for `__OBJC_METACLASS_$_`/`__OBJC_CLASS_$_` symbols. - SymbolCache classNameTable; - SymbolCache classNameRoTable; + SymbolCache classSymbolTable; + SymbolCache classRoSymbolTable; + SymbolCache classRoNameTable; /// Cache for `_OBJC_CLASS_$_` symbols stored in `__objc_stubs`. /// NOTE: Stub classes have a different layout from normal classes /// And need to be instantiated with a call to the objective-c runtime. - SymbolCache stubClassNameTable; + SymbolCache stubClassSymbolTable; - /// Cache for `L_OBJC_CLASSLIST_REFERENCES_$_` symbols. + /// Cache for `OBJC_CLASSLIST_REFERENCES_$_` symbols. SymbolCache classReferenceTable; + /// Cache for `OBJC_PROTOLIST_REFERENCES_$_` symbols. + SymbolCache protocolReferenceTable; + /// Cache for `__OBJC_PROTOCOL_$_` symbols. SymbolCache protocolTable; + SymbolCache protocolListTable; // Cache for methods. - SymbolCache methodVarNameTable; - SymbolCache methodVarRefTable; - SymbolCache methodVarTypeTable; + SymbolCache methodListTable; + SymbolCache methodNameTable; + SymbolCache methodTypeTable; + + // Cache for selectors. + SymbolCache selectorTable; // Cache for instance variables. + SymbolCache ivarTable; + SymbolCache ivarListTable; SymbolCache ivarOffsetTable; - // Gets Objective-C type tag - const char *getObjcType(Type *t); + // Generate name strings + std::string getObjcTypeEncoding(Type *t); + std::string getObjcClassRoSymbol(const ClassDeclaration& cd, bool meta); + std::string getObjcClassSymbol(const ClassDeclaration& cd, bool meta); + std::string getObjcMethodListSymbol(const ClassDeclaration& cd, bool meta); + std::string getObjcProtoSymbol(const InterfaceDeclaration& iface); + std::string getObjcIvarSymbol(const ClassDeclaration& cd, const VarDeclaration& var); + std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); + + // Generate name globals + llvm::GlobalVariable *getClassRoName(const ClassDeclaration& cd); + llvm::GlobalVariable *getProtocolName(const InterfaceDeclaration& cd); + llvm::GlobalVariable *getMethodVarName(const llvm::StringRef& name); + // Constant helpers llvm::Constant *constU32(uint32_t value); llvm::Constant *constU64(uint64_t value); llvm::Constant *constSizeT(size_t value); - llvm::GlobalVariable *getProtocoList(const InterfaceDeclaration& iface); - llvm::GlobalVariable *getProtocoList(const ClassDeclaration& cd); - llvm::GlobalVariable *getMethodListFor(const ClassDeclaration& cd, bool meta); + llvm::GlobalVariable *getEmptyCache(); - llvm::GlobalVariable *getCStringVar(const char *symbol, - const llvm::StringRef &str, - const char *section); + llvm::GlobalVariable *getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional=false); + + llvm::GlobalVariable *getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type = nullptr); + llvm::GlobalVariable *getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents); + llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section); void retain(llvm::Constant *sym); void genImageInfo(); void retainSymbols(); + + llvm::Constant *finalizeClasses(); + llvm::Constant *finalizeProtocols(); }; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 973d8ac9c2a..84b0f7ec290 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -907,7 +907,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, if (irFty.arg_objcSelector) { // Use runtime msgSend function bitcasted as original call - const char *msgSend = gABI->objcMsgSendFunc(resulttype, irFty); + const char *msgSend = gABI->objcMsgSendFunc(resulttype, irFty, directcall); callable = getRuntimeFunction(loc, gIR->module, msgSend); } diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 36af3127369..2f6903ab96f 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -703,6 +703,8 @@ llvm::ConstantPointerNull *getNullPtr() { LLConstant *getNullValue(LLType *t) { return LLConstant::getNullValue(t); } +LLConstant *wrapNull(LLConstant *v) { return v ? v : getNullPtr(); } + //////////////////////////////////////////////////////////////////////////////// size_t getTypeBitSize(LLType *t) { return gDataLayout->getTypeSizeInBits(t); } @@ -711,6 +713,10 @@ size_t getTypeStoreSize(LLType *t) { return gDataLayout->getTypeStoreSize(t); } size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); } +size_t getPointerSize() { return gDataLayout->getPointerSize(0); } + +size_t getPointerSizeInBits() { return gDataLayout->getPointerSizeInBits(0); } + unsigned int getABITypeAlign(LLType *t) { return gDataLayout->getABITypeAlign(t).value(); } diff --git a/gen/tollvm.h b/gen/tollvm.h index f8e195d2ace..6da179a3040 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -151,11 +151,14 @@ LLType *getI8Type(); LLPointerType *getOpaquePtrType(unsigned addressSpace = 0); llvm::ConstantPointerNull *getNullPtr(); LLConstant *getNullValue(LLType *t); +LLConstant *wrapNull(LLConstant *v); // type sizes size_t getTypeBitSize(LLType *t); size_t getTypeStoreSize(LLType *t); size_t getTypeAllocSize(LLType *t); +size_t getPointerSize(); +unsigned int getPointerSizeInBits(); // type alignments unsigned int getABITypeAlign(LLType *t); From 9a9f292b4fe07ba628a759c6cee6ce4b2986e59d Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 19 Nov 2024 14:40:05 +0100 Subject: [PATCH 06/67] Fix compilation issues --- dmd/objc.d | 81 ++++++++++---------- dmd/objc.h | 3 +- gen/abi/aarch64.cpp | 11 +-- gen/abi/abi.h | 6 ++ gen/abi/arm.cpp | 2 + gen/abi/x86-64.cpp | 9 ++- gen/abi/x86.cpp | 9 +-- gen/objcgen.cpp | 175 ++++++++++++++++++++++++-------------------- gen/objcgen.h | 41 ++++++----- gen/tocall.cpp | 2 +- gen/tollvm.h | 2 +- 11 files changed, 182 insertions(+), 159 deletions(-) diff --git a/dmd/objc.d b/dmd/objc.d index 331a13574bc..e1da1ccafc1 100644 --- a/dmd/objc.d +++ b/dmd/objc.d @@ -268,18 +268,13 @@ extern(C++) abstract class Objc abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const; /** - * Marks the given function declaration as a Swift stub class. - * - * A function declaration is considered optional if it's annotated with the - * UDA: `@(core.attribute.optional)`. Only function declarations inside - * interface declarations and with Objective-C linkage can be declared as - * optional. + * Marks the given class as a Swift stub class. * * Params: - * functionDeclaration = the function declaration to be set as optional + * cd = the class declaration to set as a swift stub * sc = the scope from the semantic phase */ - abstract void setAsSwiftStub(FuncDeclaration functionDeclaration, Scope* sc) const; + abstract void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const; /** * Validates function declarations declared optional. @@ -469,7 +464,7 @@ static if (!IN_LLVM) // noop } - override void setAsSwiftStub(FuncDeclaration, Scope*) const + override void setAsSwiftStub(ClassDeclaration, Scope*) const { // noop } @@ -632,40 +627,6 @@ version (IN_LLVM) {} else return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_); } - override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const - { - const count = declaredAsSwiftStubCount(cd, sc); - cd.objc.isSwiftStub = count > 0; - - if (count > 1) - .error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars); - } - - /// Returns: the number of times `cd` has been declared as optional. - private int declaredAsSwiftStubCount(ClassDeclaration cd , Scope* sc) const - { - int count; - - foreachUda(cd, sc, (e) { - if (!e.isTypeExp()) - return 0; - - auto typeExp = e.isTypeExp(); - - if (typeExp.type.ty != Tenum) - return 0; - - auto typeEnum = cast(TypeEnum) typeExp.type; - - if (isCoreUda(typeEnum.sym, Id.udaSwiftStub)) - count++; - - return 0; - }); - - return count; - } - override void setAsOptional(FuncDeclaration fd, Scope* sc) const { const count = declaredAsOptionalCount(fd, sc); @@ -883,6 +844,40 @@ version (IN_LLVM) {} else errorSupplemental(expression.loc, "`tupleof` is not available for members " ~ "of Objective-C classes. Please use the Objective-C runtime instead"); } + + override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const + { + const count = declaredAsSwiftStubCount(cd, sc); + cd.objc.isSwiftStub = count > 0; + + if (count > 1) + .error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars); + } + + /// Returns: the number of times `cd` has been declared as optional. + private int declaredAsSwiftStubCount(ClassDeclaration cd, Scope* sc) const + { + int count; + + foreachUda(cd, sc, (e) { + if (!e.isTypeExp()) + return 0; + + auto typeExp = e.isTypeExp(); + + if (typeExp.type.ty != Tenum) + return 0; + + auto typeEnum = cast(TypeEnum) typeExp.type; + + if (isCoreUda(typeEnum.sym, Id.udaSwiftStub)) + count++; + + return 0; + }); + + return count; + } } /* diff --git a/dmd/objc.h b/dmd/objc.h index 8b5f723129f..ed16e8bdda6 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -68,13 +68,14 @@ class Objc virtual void checkLinkage(FuncDeclaration* fd) = 0; virtual bool isVirtual(const FuncDeclaration*) const = 0; virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0; - virtual void setAsSwiftStub(FuncDeclaration *fd, Scope *sc) const = 0; virtual void validateOptional(FuncDeclaration *fd) const = 0; virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0; virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0; virtual AggregateDeclaration* isThis(FuncDeclaration* fd) = 0; virtual VarDeclaration* createSelectorParameter(FuncDeclaration*, Scope*) const = 0; + virtual void setAsSwiftStub(ClassDeclaration* cd, Scope *sc) const = 0; + virtual void setMetaclass(InterfaceDeclaration* id, Scope*) const = 0; virtual void setMetaclass(ClassDeclaration* id, Scope*) const = 0; virtual ClassDeclaration* getRuntimeMetaclass(ClassDeclaration* cd) = 0; diff --git a/gen/abi/aarch64.cpp b/gen/abi/aarch64.cpp index 598ca1d6816..ad3e6f1d7c9 100644 --- a/gen/abi/aarch64.cpp +++ b/gen/abi/aarch64.cpp @@ -29,12 +29,11 @@ using namespace dmd; */ struct AArch64TargetABI : TargetABI { private: - const bool isDarwin; IndirectByvalRewrite indirectByvalRewrite; ArgTypesRewrite argTypesRewrite; bool isAAPCS64VaList(Type *t) { - if (isDarwin) + if (isDarwin()) return false; // look for a __va_list struct in a `std` C++ namespace @@ -51,7 +50,7 @@ struct AArch64TargetABI : TargetABI { } public: - AArch64TargetABI() : isDarwin(global.params.targetTriple->isOSDarwin()) {} + AArch64TargetABI() {} bool returnInArg(TypeFunction *tf, bool) override { if (tf->isref()) { @@ -108,7 +107,7 @@ struct AArch64TargetABI : TargetABI { } // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1 - if (isDarwin) { + if (isDarwin()) { if (auto ts = tb->isTypeStruct()) { if (ts->sym->fields.empty() && ts->sym->isPOD()) { fty.args.erase(fty.args.begin() + i); @@ -166,7 +165,7 @@ struct AArch64TargetABI : TargetABI { } Type *vaListType() override { - if (isDarwin) + if (isDarwin()) return TargetABI::vaListType(); // char* // We need to pass the actual va_list type for correct mangling. Simply @@ -177,6 +176,8 @@ struct AArch64TargetABI : TargetABI { } const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { + assert(isDarwin()); + // see objc/message.h for objc_msgSend selection rules return superCall ? "objc_msgSendSuper" : "objc_msgSend"; } diff --git a/gen/abi/abi.h b/gen/abi/abi.h index 4f8c2362914..786a717b782 100644 --- a/gen/abi/abi.h +++ b/gen/abi/abi.h @@ -70,6 +70,7 @@ struct ABIRewrite { // interface called by codegen struct TargetABI { +public: virtual ~TargetABI() = default; /// Returns the ABI for the target we're compiling for @@ -117,6 +118,11 @@ struct TargetABI { global.params.targetTriple->getOS() == llvm::Triple::NetBSD; } + /// Returns true if the target is darwin-based. + virtual bool isDarwin() { + return global.params.targetTriple->isOSDarwin(); + } + /// Returns true if the D function uses sret (struct return). /// `needsThis` is true if the function type is for a non-static member /// function. diff --git a/gen/abi/arm.cpp b/gen/abi/arm.cpp index d46b5f794cf..05cb77f81c5 100644 --- a/gen/abi/arm.cpp +++ b/gen/abi/arm.cpp @@ -124,6 +124,8 @@ struct ArmTargetABI : TargetABI { } const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { + assert(isDarwin()); + // see objc/message.h for objc_msgSend selection rules if (fty.arg_sret) { return "objc_msgSend_stret"; diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index 1c3a5f2b98b..9c47823dac4 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -196,9 +196,6 @@ struct X86_64TargetABI : TargetABI { } }; -// The public getter for abi.cpp -TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; } - bool X86_64TargetABI::returnInArg(TypeFunction *tf, bool) { if (tf->isref()) { return false; @@ -383,8 +380,9 @@ Type *X86_64TargetABI::vaListType() { } const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) { + assert(isDarwin()); + // see objc/message.h for objc_msgSend selection rules - assert(isDarwin); if (fty.arg_sret) { return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } @@ -394,3 +392,6 @@ const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool supe } return superCall ? "objc_msgSendSuper" : "objc_msgSend"; } + +// The public getter for abi.cpp +TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; } diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index f188590612a..99169cb259c 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -23,15 +23,13 @@ using namespace dmd; struct X86TargetABI : TargetABI { - const bool isDarwin; const bool isMSVC; bool returnStructsInRegs; IntegerRewrite integerRewrite; IndirectByvalRewrite indirectByvalRewrite; X86TargetABI() - : isDarwin(global.params.targetTriple->isOSDarwin()), - isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) { + : isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) { using llvm::Triple; auto os = global.params.targetTriple->getOS(); returnStructsInRegs = @@ -230,7 +228,7 @@ struct X86TargetABI : TargetABI { // Clang does not pass empty structs, while it seems that GCC does, // at least on Linux x86. We don't know whether the C compiler will // be Clang or GCC, so just assume Clang on Darwin and G++ on Linux. - if (externD || !isDarwin) + if (externD || !isDarwin()) return; size_t i = 0; @@ -274,8 +272,9 @@ struct X86TargetABI : TargetABI { } const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { + assert(isDarwin()); + // see objc/message.h for objc_msgSend selection rules - assert(isDarwin); if (fty.arg_sret) { return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index a1ced2acdd0..a0adf12fc28 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -10,14 +10,11 @@ //===----------------------------------------------------------------------===// #include "gen/objcgen.h" - -#include "dmd/mtype.h" #include "dmd/objc.h" #include "dmd/expression.h" #include "dmd/declaration.h" #include "dmd/identifier.h" #include "gen/irstate.h" -#include "gen/tollvm.h" #include "ir/irfunction.h" bool objc_isSupported(const llvm::Triple &triple) { @@ -40,7 +37,7 @@ bool objc_isSupported(const llvm::Triple &triple) { return false; } -llvm::GlobalVariable *ObjCState::getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type = nullptr) { +llvm::GlobalVariable *ObjCState::getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type) { if (type == nullptr) type = getOpaquePtrType(); @@ -86,9 +83,17 @@ LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section) { auto init = llvm::ConstantDataArray::getString(module.getContext(), str); - auto var = new LLGlobalVariable(module, init->getType(), false, - LLGlobalValue::PrivateLinkage, init, symbol); - var->setSection(section); + auto var = new LLGlobalVariable( + module, + init->getType(), + false, + LLGlobalValue::PrivateLinkage, + init, + symbol + ); + + if (section) + var->setSection(section); return var; } @@ -96,7 +101,16 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { std::string tmp; switch (t->ty) { - case TY::Tpointer: + case TY::Tfunction: { + tmp = getObjcTypeEncoding(t->nextOf()); + + if (auto func = t->isTypeFunction()) { + for (size_t i = 0; i < func->parameterList.length(); i++) + tmp.append(getObjcTypeEncoding(func->parameterList[i]->type)); + } + return tmp; + } + case TY::Tpointer: { // C string (char*) if (t->nextOf()->ty == TY::Tchar) @@ -105,7 +119,8 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { tmp.append("^"); tmp.append(getObjcTypeEncoding(t->nextOf())); return tmp; - case TY::Tsarray: + } + case TY::Tsarray: { // Static arrays are encoded in the form of: // [] @@ -116,7 +131,8 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { tmp.append(getObjcTypeEncoding(typ->next)); tmp.append("]"); return tmp; - case TY::Tstruct: + } + case TY::Tstruct: { // Structs are encoded in the form of: // {=} @@ -135,6 +151,7 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { tmp.append(isUnion ? ")" : "}"); return tmp; + } case TY::Tvoid: return "v"; case TY::Tbool: return "B"; case TY::Tint8: return "c"; @@ -154,11 +171,12 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { case TY::Tcomplex64: return "jd"; case TY::Tfloat80: return "D"; case TY::Tcomplex80: return "jD"; - case TY::Tclass: + case TY::Tclass: { if (auto klass = t->isTypeClass()) { return klass->sym->classKind == ClassKind::objc ? "@" : "?"; } return "?"; + } default: return "?"; // unknown } } @@ -186,7 +204,12 @@ std::string ObjCState::getObjcProtoSymbol(const InterfaceDeclaration& iface) { } std::string ObjCState::getObjcIvarSymbol(const ClassDeclaration& cd, const VarDeclaration& var) { - return std::string("OBJC_IVAR_$_", cd.objc.identifier->toChars(), ".", var.iden->toChars()); + std::string tmp; + tmp.append("OBJC_IVAR_$_"); + tmp.append(cd.objc.identifier->toChars()); + tmp.append("."); + tmp.append(var.ident->toChars()); + return tmp; } std::string ObjCState::getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { @@ -197,15 +220,8 @@ std::string ObjCState::getObjcSymbolName(const char *dsymPrefix, const char *dsy // UTILITIES // -LLGlobalVariable *ObjCState::getTypeEncoding(const Declaration& decl) { - std::string out_ = getObjcTypeEncoding(decl.type->nextOf()); - - if (decl.parameters) { - for (size_t i = 0; i < decl.parameters->length; i++) - out_.append(getObjcTypeEncoding(decl.parameters[i]->type)); - } - - return getMethodVarType(out_); +LLGlobalVariable *ObjCState::getTypeEncoding(Type *t) { + return getMethodVarTypeName(getObjcTypeEncoding(t)); } llvm::GlobalVariable* ObjCState::getEmptyCache() { @@ -215,7 +231,7 @@ llvm::GlobalVariable* ObjCState::getEmptyCache() { return g; } -llvm::Value *ObjCState::unmaskPointer(llvm::Value *value) { +LLValue *ObjCState::unmaskPointer(LLValue *value) { return gIR->ir->CreateAnd(value, OBJC_PTRMASK); } @@ -376,7 +392,7 @@ LLGlobalVariable *ObjCState::getClassReference(const ClassDeclaration& cd) { return it->second; } - auto gvar = getClassSymbol(cd); + auto gvar = getClassSymbol(cd, false); auto classref = new LLGlobalVariable( module, gvar->getType(), false, @@ -403,14 +419,14 @@ LLGlobalVariable *ObjCState::getClassReference(const ClassDeclaration& cd) { // PROTOCOLS // -LLGlobalVariable *ObjCState::getMethodName(const llvm::StringRef &name) { - auto it = methodNameTable.find(name); - if (it != methodNameTable.end()) { +LLGlobalVariable *ObjCState::getProtocolName(const llvm::StringRef &name) { + auto it = protocolNameTable.find(name); + if (it != protocolNameTable.end()) { return it->second; } - auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, methodNameSection); - methodNameTable[name] = var; + auto var = getCStringVar("", name); + classRoNameTable[name] = var; retain(var); return var; } @@ -428,9 +444,9 @@ LLGlobalVariable *ObjCState::getProtocolListFor(const ClassDeclaration& cd) { // protocol_ref_t list[0]; // variable-size // } std::vector members; - members.push_back(constU64(cd.interfaces.length)); - for(size_t i; i < cd.interfaces.length; i++) { - auto iface = cd.interfaces[i]->sym->isInterfaceDeclaration(); + members.push_back(DtoConstUlong(cd.interfaces.length)); + for(size_t i = 0; i < cd.interfaces.length; i++) { + auto iface = cd.interfaces.ptr[i]->sym->isInterfaceDeclaration(); members.push_back(getProtocolReference(*iface)); } @@ -464,12 +480,10 @@ LLGlobalVariable *ObjCState::getProtocolSymbol(const InterfaceDeclaration& iface // uint32_t flags; // } std::vector members; - size_t protocolTSize = (getPointerSize()*8)+8; - auto nameConst = llvm::ConstantDataArray::getString(module.getContext(), name); members.push_back(getNullPtr()); // unused? - members.push_back(nameConst); // mangledName + members.push_back(getProtocolName(name)); // mangledName members.push_back(getProtocolListFor(iface)); // protocols members.push_back(getMethodListFor(iface, false)); // instanceMethods members.push_back(getMethodListFor(iface, true)); // classMethods @@ -482,6 +496,7 @@ LLGlobalVariable *ObjCState::getProtocolSymbol(const InterfaceDeclaration& iface auto var = getGlobalWithBytes(module, name, members); protocolTable[name] = var; retain(var); + return var; } LLGlobalVariable *ObjCState::getProtocolReference(const InterfaceDeclaration& iface) { @@ -511,32 +526,30 @@ LLGlobalVariable *ObjCState::getProtocolReference(const InterfaceDeclaration& if // // INSTANCE VARIABLES // -LLGlobalVariable *ObjCState::getIVarListFor(const ClassDeclaration& cd) { +LLConstant *ObjCState::getIVarListFor(const ClassDeclaration& cd) { + + // If there's no fields, just return null. + if (cd.fields.length > 0) { + return getNullPtr(); + } + auto name = getObjcSymbolName("OBJC_$_INSTANCE_VARIABLES_", cd.objc.identifier->toChars()); auto it = ivarListTable.find(name); if (it != ivarListTable.end()) { return it->second; } - // If there's no fields, just return a null constant. - if (cd.fields.length > 0) { - - auto var = getNullPtr(); - ivarListTable[name] = var; - return var; - } - ConstantList members; members.push_back(DtoConstUint(OBJC_IVAR_ENTSIZE)); members.push_back(DtoConstUint(cd.fields.length)); - for(size_t i; i < cd.fields.length; i++) { - if (auto vd = cd.fields[i]->isVarDeclaration()) ( + for(size_t i = 0; i < cd.fields.length; i++) { + if (auto vd = cd.fields[i]->isVarDeclaration()) { members.push_back(getIVarSymbol(cd, *vd)); - ) + } } - auto var = getGlobalWithBytes(module, globalName, members); + auto var = getGlobalWithBytes(module, name, members); var->setSection(constSection); ivarListTable[name] = var; retain(var); @@ -561,40 +574,39 @@ LLGlobalVariable *ObjCState::getIVarSymbol(const ClassDeclaration& cd, const Var ConstantList members; members.push_back(getIVarOffset(cd, var, false)); members.push_back(getMethodVarName(var.ident->toChars())); - members.push_back(getTypeEncoding(var.type)); + members.push_back(getMethodVarTypeName(getObjcTypeEncoding(var.type))); members.push_back(DtoConstUint(var.alignment.isDefault() ? -1 : var.alignment.get())); - members.push_back(DtoConstUint(var.size(var.loc))); + members.push_back(DtoConstUint(const_cast(var).size(var.loc))); - auto var = getGlobalWithBytes(module, name, members); - ivarTable[name] = var; - retain(var); - return var; + auto retval = getGlobalWithBytes(module, name, members); + ivarTable[name] = retval; + retain(retval); + return retval; } llvm::GlobalVariable *ObjCState::getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol) { - auto nameBuf = std::string("OBJC_IVAR_$_", cd.objc.identifier->toChars(), ".", var.iden->toChars()); - auto name = llvm::StringRef(nameBuf); + auto name = getObjcIvarSymbol(cd, var); auto it = ivarOffsetTable.find(name); if (it != ivarOffsetTable.end()) { return it->second; } - LLGlobalVariable *var; + LLGlobalVariable *retval; if (cd.objc.isMeta) { - var = getGlobal(module, name); - ivarOffsetTable[name] = var; - retain(var); - return var; + retval = getGlobal(module, name); + ivarOffsetTable[name] = retval; + retain(retval); + return retval; } ConstantList members; members.push_back(DtoConstUlong(var.offset)); - var = getGlobalWithBytes(module, name, members); - ivarOffsetTable[name] = var; - retain(var); - return var; + retval = getGlobalWithBytes(module, name, members); + ivarOffsetTable[name] = retval; + retain(retval); + return retval; } // @@ -613,7 +625,7 @@ LLGlobalVariable *ObjCState::getMethodVarName(const llvm::StringRef &name) { return var; } -LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional=false) { +LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional) { llvm::StringRef name(cd.objc.identifier->toChars()); auto it = methodListTable.find(name); if (it != methodListTable.end()) { @@ -623,9 +635,9 @@ LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool m auto methods = meta ? cd.objc.metaclass->objc.methodList : cd.objc.methodList; // Count the amount of methods with a body. - size_t methodCount; - for(size_t i; i < methods.length; i++) { - if (methods[i]->fbody) methodCount++; + size_t methodCount = 0; + for(size_t i = 0; i < methods.length; i++) { + if (methods.ptr[i]->fbody) methodCount++; } // Empty classes don't need a method list generated. @@ -637,15 +649,15 @@ LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool m // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L93 members.push_back(DtoConstUint(OBJC_METHOD_SIZEOF)); members.push_back(DtoConstUint(methodCount)); - for(size_t i; i < methods.length; i++) { - if (methods[i]->fbody) { - auto selector = methods[i]->objc.selector; + for(size_t i = 0; i < methods.length; i++) { + if (methods.ptr[i]->fbody) { + auto selector = methods.ptr[i]->objc.selector; // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L207 llvm::StringRef name(selector->stringvalue, selector->stringlen); - members.push_back(getMethodSymbol(name)); - members.push_back(getMethodType(name)); - members.push_back(DtoCallee(method)); + members.push_back(getMethodVarName(name)); + members.push_back(getMethodVarTypeName(name)); + members.push_back(DtoCallee(methods.ptr[i])); } } @@ -655,13 +667,18 @@ LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool m return var; } -LLGlobalVariable *ObjCState::getMethodVarType(const llvm::StringRef& name) { +LLGlobalVariable *ObjCState::getMethodVarType(const FuncDeclaration& fd) { + return getTypeEncoding(fd.type); +} + + +LLGlobalVariable *ObjCState::getMethodVarTypeName(const llvm::StringRef& name) { auto it = methodTypeTable.find(name); if (it != methodTypeTable.end()) { return it->second; } - auto var = getCStringVar("OBJC_METH_VAR_TYPE_", encoding, methodTypeSection); + auto var = getCStringVar("OBJC_METH_VAR_TYPE_", name, methodTypeSection); methodTypeTable[name] = var; retain(var); return var; @@ -674,7 +691,7 @@ LLGlobalVariable *ObjCState::getSelector(const ObjcSelector &sel) { return it->second; } - auto gvar = getMethodName(name); + auto gvar = getMethodVarName(name); auto selref = new LLGlobalVariable( module, gvar->getType(), false, // prevent const elimination optimization @@ -707,7 +724,7 @@ llvm::Constant *ObjCState::finalizeClasses() { for(auto classRef = classes.begin(); classRef != classes.end(); ++classRef) { auto klass = *classRef; if (!klass->objc.isExtern && !klass->objc.isMeta) { - members.push_back(getClassSymbol(*klass)); + members.push_back(getClassSymbol(*klass, false)); } } @@ -752,7 +769,7 @@ void ObjCState::genImageInfo() { module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", - llvm::MDString::get(module.getContext(), imageInfoSectionName)); + llvm::MDString::get(module.getContext(), imageInfoSection)); module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags } diff --git a/gen/objcgen.h b/gen/objcgen.h index 55deec96bd5..a84b171654c 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -15,6 +15,8 @@ #include #include "llvm/ADT/StringMap.h" +#include "gen/tollvm.h" +#include "dmd/mtype.h" struct ObjcSelector; namespace llvm { @@ -25,11 +27,13 @@ class Triple; } // Forward decl. +class Declaration; class ClassDeclaration; class FuncDeclaration; class InterfaceDeclaration; class VarDeclaration; class Identifier; +class Type; typedef llvm::StringMap SymbolCache; typedef std::vector ConstantList; @@ -57,33 +61,33 @@ class ObjCState { ObjCState(llvm::Module &module) : module(module) { } // General - llvm::GlobalVariable *getTypeEncoding(const Declaration& ty); + LLGlobalVariable *getTypeEncoding(Type *t); // Classes - llvm::GlobalVariable *getClassSymbol(const ClassDeclaration& cd, bool meta); - llvm::GlobalVariable *getClassRoSymbol(const ClassDeclaration& cd, bool meta); - llvm::GlobalVariable *getClassReference(const ClassDeclaration& cd); + LLGlobalVariable *getClassSymbol(const ClassDeclaration& cd, bool meta); + LLGlobalVariable *getClassRoSymbol(const ClassDeclaration& cd, bool meta); + LLGlobalVariable *getClassReference(const ClassDeclaration& cd); // Categories // Interface variables - llvm::GlobalVariable *getIVarListFor(const ClassDeclaration& cd); - llvm::GlobalVariable *getIVarSymbol(const ClassDeclaration& cd, const VarDeclaration& var); - llvm::GlobalVariable *getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol); + LLConstant *getIVarListFor(const ClassDeclaration& cd); + LLGlobalVariable *getIVarSymbol(const ClassDeclaration& cd, const VarDeclaration& var); + LLGlobalVariable *getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol); // Methods - llvm::GlobalVariable *getMethodVarType(const llvm::StringRef& ty); + LLGlobalVariable *getMethodVarType(const FuncDeclaration& fd); // Selector - llvm::GlobalVariable *getSelector(const ObjcSelector &sel); + LLGlobalVariable *getSelector(const ObjcSelector &sel); // Protocols - llvm::GlobalVariable *getProtocolListFor(const ClassDeclaration& cd); - llvm::GlobalVariable *getProtocolSymbol(const InterfaceDeclaration& id); - llvm::GlobalVariable *getProtocolReference(const InterfaceDeclaration& id); + LLGlobalVariable *getProtocolListFor(const ClassDeclaration& cd); + LLGlobalVariable *getProtocolSymbol(const InterfaceDeclaration& id); + LLGlobalVariable *getProtocolReference(const InterfaceDeclaration& id); // Unmasks pointer to make sure it's not a tagged pointer. - llvm::Value *unmaskPointer(llvm::Value *value); + LLValue *unmaskPointer(LLValue *value); void finalize(); @@ -137,6 +141,7 @@ class ObjCState { /// Cache for `__OBJC_PROTOCOL_$_` symbols. SymbolCache protocolTable; + SymbolCache protocolNameTable; SymbolCache protocolListTable; // Cache for methods. @@ -163,13 +168,9 @@ class ObjCState { // Generate name globals llvm::GlobalVariable *getClassRoName(const ClassDeclaration& cd); - llvm::GlobalVariable *getProtocolName(const InterfaceDeclaration& cd); + llvm::GlobalVariable *getProtocolName(const llvm::StringRef& name); llvm::GlobalVariable *getMethodVarName(const llvm::StringRef& name); - - // Constant helpers - llvm::Constant *constU32(uint32_t value); - llvm::Constant *constU64(uint64_t value); - llvm::Constant *constSizeT(size_t value); + llvm::GlobalVariable *getMethodVarTypeName(const llvm::StringRef& name); llvm::GlobalVariable *getEmptyCache(); @@ -177,7 +178,7 @@ class ObjCState { llvm::GlobalVariable *getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type = nullptr); llvm::GlobalVariable *getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents); - llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section); + llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section = nullptr); void retain(llvm::Constant *sym); diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 84b0f7ec290..0b5b340ec3e 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -795,7 +795,7 @@ class ImplicitArgumentsBuilder { const auto selector = dfnval->func->objc.selector; assert(selector); - LLGlobalVariable *selptr = gIR->objc.getMethodVarRef(*selector); + LLGlobalVariable *selptr = gIR->objc.getSelector(*selector); args.push_back(DtoLoad(selptr->getValueType(), selptr)); } } diff --git a/gen/tollvm.h b/gen/tollvm.h index 6da179a3040..f4c298ca961 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -158,7 +158,7 @@ size_t getTypeBitSize(LLType *t); size_t getTypeStoreSize(LLType *t); size_t getTypeAllocSize(LLType *t); size_t getPointerSize(); -unsigned int getPointerSizeInBits(); +size_t getPointerSizeInBits(); // type alignments unsigned int getABITypeAlign(LLType *t); From b2c30f6599ed26f1bb14aaf4a076e83c308ca2f9 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Wed, 20 Nov 2024 03:50:08 +0100 Subject: [PATCH 07/67] Fix objc ir codegen --- gen/objcgen.cpp | 21 +++++++++++++++------ gen/objcgen.h | 26 +++++++++++++------------- gen/toir.cpp | 22 ++++++++++++++++++++-- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index a0adf12fc28..d6f9be68200 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -56,10 +56,17 @@ llvm::GlobalVariable *ObjCState::getGlobal(llvm::Module& module, llvm::StringRef return var; } -llvm::GlobalVariable *ObjCState::getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents) { - auto init = llvm::ConstantStruct::getAnon( - packedContents, - true +LLGlobalVariable *ObjCState::getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents) { + if (packedContents.empty()) { + auto null_ = llvm::ConstantPointerNull::get( + LLPointerType::get(module.getContext(), 0) + ); + packedContents.push_back(null_); + } + + auto init = LLConstantStruct::getAnon( + packedContents, + true ); auto var = new LLGlobalVariable( @@ -213,7 +220,7 @@ std::string ObjCState::getObjcIvarSymbol(const ClassDeclaration& cd, const VarDe } std::string ObjCState::getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { - return std::string(dsymPrefix, dsymName); + return (std::string(dsymPrefix) + std::string(dsymName)); } // @@ -251,7 +258,9 @@ unsigned int getClassFlags(const ClassDeclaration& cd) { } LLGlobalVariable *ObjCState::getClassSymbol(const ClassDeclaration& cd, bool meta) { - llvm::StringRef name(cd.toChars()); + + auto csym = getObjcClassSymbol(cd, meta); + llvm::StringRef name(csym); auto it = classSymbolTable.find(name); if (it != classSymbolTable.end()) { return it->second; diff --git a/gen/objcgen.h b/gen/objcgen.h index a84b171654c..2adf9db731d 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -92,25 +92,25 @@ class ObjCState { void finalize(); // Section Names - const char *imageInfoSection = "__DATA,__objc_imageinfo, regular, no_dead_strip"; + const char *imageInfoSection = "__DATA,__objc_imageinfo, regular"; // Lists - const char *classListSection = "__DATA,__objc_classlist, regular, no_dead_strip"; - const char *protoListSection = "__DATA,__objc_protolist, regular, no_dead_strip"; - const char *catListSection = "__DATA,__objc_catlist, regular, no_dead_strip"; + const char *classListSection = "__DATA,__objc_classlist, regular"; + const char *protoListSection = "__DATA,__objc_protolist, regular"; + const char *catListSection = "__DATA,__objc_catlist, regular"; - const char *classNameSection = "__TEXT,__objc_classname, cstring_literals, regular, no_dead_strip"; - const char *classStubsSection = "__DATA,__objc_stubs, regular, no_dead_strip"; + const char *classNameSection = "__TEXT,__objc_classname, cstring_literals"; + const char *classStubsSection = "__DATA,__objc_stubs, regular"; - const char *constSection = "__DATA,__objc_const, regular, no_dead_strip"; - const char *dataSection = "__DATA,__objc_data, regular, no_dead_strip"; + const char *constSection = "__DATA,__objc_const, regular"; + const char *dataSection = "__DATA,__objc_data, regular"; - const char *methodNameSection = "__TEXT,__objc_methname, cstring_literals, regular, no_dead_strip"; - const char *methodTypeSection = "__TEXT,__objc_methtype, regular, no_dead_strip"; + const char *methodNameSection = "__TEXT,__objc_methname, cstring_literals"; + const char *methodTypeSection = "__TEXT,__objc_methtype, regular"; - const char *classRefsSection = "__DATA,__objc_classrefs, regular, no_dead_strip"; - const char *protoRefsSection = "__DATA,__objc_protorefs, regular, no_dead_strip"; - const char *selectorRefsSection = "__DATA,__objc_selrefs, regular, no_dead_strip"; + const char *classRefsSection = "__DATA,__objc_classrefs, regular"; + const char *protoRefsSection = "__DATA,__objc_protorefs, regular"; + const char *selectorRefsSection = "__DATA,__objc_selrefs, regular"; private: llvm::Module &module; diff --git a/gen/toir.cpp b/gen/toir.cpp index 6876c7457fc..8f4692b8e57 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -329,6 +329,25 @@ class ToElemVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////////// + void visit(ObjcClassReferenceExp *e) override { + IF_LOG Logger::print("ObjcClassReferenceExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + LLGlobalVariable *decl; + if (auto iface = e->classDeclaration->isInterfaceDeclaration()) { + + auto loaded = DtoLoad(DtoType(e->type), gIR->objc.getProtocolReference(*iface)); + result = new DImValue(e->type, loaded); + return; + } + + auto loaded = DtoLoad(DtoType(e->type), gIR->objc.getClassReference(*e->classDeclaration)); + result = new DImValue(e->type, loaded); + } + + ////////////////////////////////////////////////////////////////////////////// + void visit(VarExp *e) override { IF_LOG Logger::print("VarExp::toElem: %s @ %s\n", e->toChars(), e->type->toChars()); @@ -1017,10 +1036,9 @@ class ToElemVisitor : public Visitor { auto &PGO = gIR->funcGen().pgo; PGO.setCurrentStmt(e); - DValue *l = toElem(e->e1); - Type *e1type = e->e1->type->toBasetype(); + DValue *l = toElem(e->e1); if (VarDeclaration *vd = e->var->isVarDeclaration()) { AggregateDeclaration *ad; LLValue *aggrPtr; From 3679436eafd541e6273441813ba456e7df9258e8 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Wed, 20 Nov 2024 03:50:41 +0100 Subject: [PATCH 08/67] Add objc linker option --- driver/linker-gcc.cpp | 13 +++++++++++++ driver/linker.cpp | 8 ++++++++ driver/linker.h | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index a0aad16a972..9ac3691c4e7 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -48,6 +48,10 @@ static llvm::cl::opt linkNoCpp( "link-no-cpp", llvm::cl::ZeroOrMore, llvm::cl::Hidden, llvm::cl::desc("Disable automatic linking with the C++ standard library.")); +static llvm::cl::opt linkNoObjc( + "link-no-objc", llvm::cl::ZeroOrMore, llvm::cl::Hidden, + llvm::cl::desc("Disable automatic linking with the Objective-C runtime library.")); + ////////////////////////////////////////////////////////////////////////////// namespace { @@ -72,6 +76,7 @@ class ArgsBuilder { virtual void addXRayLinkFlags(const llvm::Triple &triple); virtual bool addCompilerRTArchiveLinkFlags(llvm::StringRef baseName, const llvm::Triple &triple); + virtual void addObjcStdlibLinkFlags(const llvm::Triple &triple); virtual void addLinker(); virtual void addUserSwitches(); @@ -467,6 +472,13 @@ void ArgsBuilder::addCppStdlibLinkFlags(const llvm::Triple &triple) { } } +void ArgsBuilder::addObjcStdlibLinkFlags(const llvm::Triple &triple) { + if (linkNoObjc) + return; + + args.push_back(("-l"+getObjcLibName()).str()); +} + // Adds all required link flags for PGO. void ArgsBuilder::addProfileRuntimeLinkFlags(const llvm::Triple &triple) { const auto searchPaths = @@ -710,6 +722,7 @@ void ArgsBuilder::addDefaultPlatformLibs() { addSoname = true; args.push_back("-lpthread"); args.push_back("-lm"); + this->addObjcStdlibLinkFlags(triple); break; case llvm::Triple::Solaris: diff --git a/driver/linker.cpp b/driver/linker.cpp index bd309e83b71..b593994b063 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -257,6 +257,14 @@ llvm::StringRef getMscrtLibName(const bool *useInternalToolchain) { ////////////////////////////////////////////////////////////////////////////// +llvm::StringRef getObjcLibName() { + // TODO: Support scanning for and loading alternate + // objective-c runtimes like the GNUStep runtime? + return "objc"; +} + +////////////////////////////////////////////////////////////////////////////// + /// Insert an LLVM bitcode file into the module static void insertBitcodeIntoModule(const char *bcFile, llvm::Module &M, llvm::LLVMContext &Context) { diff --git a/driver/linker.h b/driver/linker.h index 28362d86526..78478af0f2e 100644 --- a/driver/linker.h +++ b/driver/linker.h @@ -58,6 +58,11 @@ llvm::StringRef getExplicitMscrtLibName(); */ llvm::StringRef getMscrtLibName(const bool *useInternalToolchain = nullptr); +/** + * Returns the name of the Objective-C runtime library to link with. + */ +llvm::StringRef getObjcLibName(); + /** * Inserts bitcode files passed on the commandline into a module. */ From 9cbd156fc0690eb0fc4937193d85c0e200b971d0 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Wed, 20 Nov 2024 04:25:11 +0100 Subject: [PATCH 09/67] Add swift stub classref get ir gen --- gen/objcgen.cpp | 10 +++++++++- gen/objcgen.h | 3 ++- gen/toir.cpp | 8 +++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index d6f9be68200..07c2e7925bd 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -15,6 +15,7 @@ #include "dmd/declaration.h" #include "dmd/identifier.h" #include "gen/irstate.h" +#include "gen/runtime.h" #include "ir/irfunction.h" bool objc_isSupported(const llvm::Triple &triple) { @@ -394,7 +395,14 @@ LLGlobalVariable *ObjCState::getClassRoSymbol(const ClassDeclaration& cd, bool m return var; } -LLGlobalVariable *ObjCState::getClassReference(const ClassDeclaration& cd) { +LLValue *ObjCState::getSwiftStubClassReference(const ClassDeclaration& cd) { + auto classref = getClassReference(cd); + auto toClassRefFunc = getRuntimeFunction(cd.loc, module, "objc_loadClassRef"); + auto retv = gIR->CreateCallOrInvoke(toClassRefFunc, classref, ""); + return retv; +} + +LLConstant *ObjCState::getClassReference(const ClassDeclaration& cd) { llvm::StringRef name(cd.objc.identifier->toChars()); auto it = classReferenceTable.find(name); if (it != classReferenceTable.end()) { diff --git a/gen/objcgen.h b/gen/objcgen.h index 2adf9db731d..7b05edcfcb3 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -66,7 +66,8 @@ class ObjCState { // Classes LLGlobalVariable *getClassSymbol(const ClassDeclaration& cd, bool meta); LLGlobalVariable *getClassRoSymbol(const ClassDeclaration& cd, bool meta); - LLGlobalVariable *getClassReference(const ClassDeclaration& cd); + LLConstant *getClassReference(const ClassDeclaration& cd); + LLValue *getSwiftStubClassReference(const ClassDeclaration& cd); // Categories diff --git a/gen/toir.cpp b/gen/toir.cpp index 8f4692b8e57..dc3822ee338 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -334,7 +334,7 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; - LLGlobalVariable *decl; + // Protocols if (auto iface = e->classDeclaration->isInterfaceDeclaration()) { auto loaded = DtoLoad(DtoType(e->type), gIR->objc.getProtocolReference(*iface)); @@ -342,6 +342,12 @@ class ToElemVisitor : public Visitor { return; } + // Swift stub classes + if (e->classDeclaration->objc.isSwiftStub) { + result = new DImValue(e->type, gIR->objc.getSwiftStubClassReference(*e->classDeclaration)); + return; + } + auto loaded = DtoLoad(DtoType(e->type), gIR->objc.getClassReference(*e->classDeclaration)); result = new DImValue(e->type, loaded); } From 4246d42b4a53c6c10cc0a6393ba0277e2348c89c Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Wed, 20 Nov 2024 06:12:42 +0100 Subject: [PATCH 10/67] Minor cleanup --- driver/linker-gcc.cpp | 2 +- gen/objcgen.cpp | 60 ++++++++++++++++++++++++++++++------------- gen/objcgen.h | 31 +++++++++++----------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 9ac3691c4e7..26197b3d397 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -715,6 +715,7 @@ void ArgsBuilder::addDefaultPlatformLibs() { // fallthrough case llvm::Triple::Darwin: case llvm::Triple::MacOSX: + this->addObjcStdlibLinkFlags(triple); case llvm::Triple::FreeBSD: case llvm::Triple::NetBSD: case llvm::Triple::OpenBSD: @@ -722,7 +723,6 @@ void ArgsBuilder::addDefaultPlatformLibs() { addSoname = true; args.push_back("-lpthread"); args.push_back("-lm"); - this->addObjcStdlibLinkFlags(triple); break; case llvm::Triple::Solaris: diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 07c2e7925bd..e8d90b3f71d 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -196,25 +196,25 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { // std::string ObjCState::getObjcClassRoSymbol(const ClassDeclaration& cd, bool meta) { - return getObjcSymbolName(meta ? "OBJC_METACLASS_RO_$_" : "OBJC_CLASS_RO_$_", cd.toChars()); + return getObjcSymbolName(meta ? "OBJC_METACLASS_RO_$_" : "OBJC_CLASS_RO_$_", cd.ident->toChars()); } std::string ObjCState::getObjcClassSymbol(const ClassDeclaration& cd, bool meta) { - return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", cd.toChars()); + return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", cd.ident->toChars()); } std::string ObjCState::getObjcMethodListSymbol(const ClassDeclaration& cd, bool meta) { return getObjcSymbolName(meta ? "OBJC_$_CLASS_METHODS_" : "OBJC_$_INSTANCE_METHODS_", cd.objc.identifier->toChars()); } -std::string ObjCState::getObjcProtoSymbol(const InterfaceDeclaration& iface) { - return getObjcSymbolName("OBJC_PROTOCOL_$_", iface.toChars()); +std::string ObjCState::getObjcProtoSymbol(const InterfaceDeclaration& id) { + return getObjcSymbolName("OBJC_PROTOCOL_$_", id.ident->toChars()); } std::string ObjCState::getObjcIvarSymbol(const ClassDeclaration& cd, const VarDeclaration& var) { std::string tmp; tmp.append("OBJC_IVAR_$_"); - tmp.append(cd.objc.identifier->toChars()); + tmp.append(cd.ident->toChars()); tmp.append("."); tmp.append(var.ident->toChars()); return tmp; @@ -329,7 +329,7 @@ LLGlobalVariable *ObjCState::getClassSymbol(const ClassDeclaration& cd, bool met } LLGlobalVariable *ObjCState::getClassRoName(const ClassDeclaration& cd) { - llvm::StringRef name(cd.toChars()); + llvm::StringRef name(cd.ident->toChars()); auto it = classRoNameTable.find(name); if (it != classRoNameTable.end()) { return it->second; @@ -341,6 +341,33 @@ LLGlobalVariable *ObjCState::getClassRoName(const ClassDeclaration& cd) { return var; } +ptrdiff_t getInstanceStart(ClassDeclaration& cd, bool meta) { + if (meta) + return getPointerSizeInBits() == 64 ? + OBJC_METACLASS_INSTANCESTART_64 : OBJC_METACLASS_INSTANCESTART_32; + + ptrdiff_t start = cd.size(cd.loc); + if (!cd.members || cd.members->length == 0) + return start; + + for(d_size_t idx = 0; idx < cd.members->length; idx++) + { + auto var = ((*cd.members)[idx])->isVarDeclaration(); + + if (var && var->isField()) + return var->offset; + } + return start; +} + +size_t getInstanceSize(ClassDeclaration& cd, bool meta) { + if (meta) + return getPointerSizeInBits() == 64 ? + OBJC_METACLASS_INSTANCESTART_64 : OBJC_METACLASS_INSTANCESTART_32; + + return cd.size(cd.loc); +} + LLGlobalVariable *ObjCState::getClassRoSymbol(const ClassDeclaration& cd, bool meta) { auto name = getObjcClassRoSymbol(cd, meta); auto it = classRoSymbolTable.find(name); @@ -364,8 +391,8 @@ LLGlobalVariable *ObjCState::getClassRoSymbol(const ClassDeclaration& cd, bool m // }; std::vector members; members.push_back(DtoConstUint(getClassFlags(cd))); // flags - members.push_back(DtoConstUint(0)); // instanceStart - members.push_back(DtoConstUint(0)); // instanceSize + members.push_back(DtoConstUint(getInstanceStart(const_cast(cd), meta))); // instanceStart + members.push_back(DtoConstUint(getInstanceSize(const_cast(cd), meta))); // instanceSize if (getPointerSizeInBits() == 64) members.push_back(DtoConstUint(0)); // reserved members.push_back(getNullPtr()); // ivarLayout @@ -497,7 +524,7 @@ LLGlobalVariable *ObjCState::getProtocolSymbol(const InterfaceDeclaration& iface // uint32_t flags; // } std::vector members; - size_t protocolTSize = (getPointerSize()*8)+8; + size_t protocolTSize = (getPointerSize()*9)+8; members.push_back(getNullPtr()); // unused? members.push_back(getProtocolName(name)); // mangledName @@ -546,7 +573,7 @@ LLGlobalVariable *ObjCState::getProtocolReference(const InterfaceDeclaration& if LLConstant *ObjCState::getIVarListFor(const ClassDeclaration& cd) { // If there's no fields, just return null. - if (cd.fields.length > 0) { + if (cd.fields.empty()) { return getNullPtr(); } @@ -688,14 +715,13 @@ LLGlobalVariable *ObjCState::getMethodVarType(const FuncDeclaration& fd) { return getTypeEncoding(fd.type); } - LLGlobalVariable *ObjCState::getMethodVarTypeName(const llvm::StringRef& name) { auto it = methodTypeTable.find(name); if (it != methodTypeTable.end()) { return it->second; } - auto var = getCStringVar("OBJC_METH_VAR_TYPE_", name, methodTypeSection); + auto var = getCStringVar("OBJC_METH_VAR_TYPE", name, methodTypeSection); methodTypeTable[name] = var; retain(var); return var; @@ -745,7 +771,7 @@ llvm::Constant *ObjCState::finalizeClasses() { } } - auto var = getGlobalWithBytes(module, "OBJC_CLASS_$_", members); + auto var = getGlobalWithBytes(module, "L_OBJC_LABEL_CLASS_$", members); var->setSection(classListSection); return var; } @@ -762,7 +788,7 @@ llvm::Constant *ObjCState::finalizeProtocols() { } } - auto var = getGlobalWithBytes(module, "OBJC_PROTOCOL_$_", members); + auto var = getGlobalWithBytes(module, "L_OBJC_LABEL_PROTOCOL_$", members); var->setSection(protoListSection); return var; } @@ -783,12 +809,10 @@ void ObjCState::finalize() { void ObjCState::genImageInfo() { // Use LLVM to generate image info module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2); // Only support ABI 2. (Non-fragile) - module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", - 0u); // version + module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", llvm::MDString::get(module.getContext(), imageInfoSection)); - module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", - 0u); // flags + module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags } void ObjCState::retainSymbols() { diff --git a/gen/objcgen.h b/gen/objcgen.h index 7b05edcfcb3..d5d7b3c6fed 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -48,6 +48,8 @@ bool objc_isSupported(const llvm::Triple &triple); // poking the Objective-C library? #define OBJC_METHOD_SIZEOF 24 #define OBJC_IVAR_ENTSIZE 32 +#define OBJC_METACLASS_INSTANCESTART_32 40 +#define OBJC_METACLASS_INSTANCESTART_64 76 // class is a metaclass #define RO_META (1<<0) @@ -78,6 +80,7 @@ class ObjCState { // Methods LLGlobalVariable *getMethodVarType(const FuncDeclaration& fd); + LLGlobalVariable *getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional=false); // Selector LLGlobalVariable *getSelector(const ObjcSelector &sel); @@ -93,25 +96,25 @@ class ObjCState { void finalize(); // Section Names - const char *imageInfoSection = "__DATA,__objc_imageinfo, regular"; + const char *imageInfoSection = "__DATA,__objc_imageinfo,regular,no_dead_strip"; // Lists - const char *classListSection = "__DATA,__objc_classlist, regular"; - const char *protoListSection = "__DATA,__objc_protolist, regular"; - const char *catListSection = "__DATA,__objc_catlist, regular"; + const char *classListSection = "__DATA,__objc_classlist,regular,no_dead_strip"; + const char *protoListSection = "__DATA,__objc_protolist,regular,no_dead_strip"; + const char *catListSection = "__DATA,__objc_catlist,regular,no_dead_strip"; - const char *classNameSection = "__TEXT,__objc_classname, cstring_literals"; - const char *classStubsSection = "__DATA,__objc_stubs, regular"; + const char *classNameSection = "__TEXT,__objc_classname,cstring_literals,no_dead_strip"; + const char *classStubsSection = "__DATA,__objc_stubs,regular,no_dead_strip"; - const char *constSection = "__DATA,__objc_const, regular"; - const char *dataSection = "__DATA,__objc_data, regular"; + const char *constSection = "__DATA,__objc_const,regular,no_dead_strip"; + const char *dataSection = "__DATA,__objc_data,regular,no_dead_strip"; - const char *methodNameSection = "__TEXT,__objc_methname, cstring_literals"; - const char *methodTypeSection = "__TEXT,__objc_methtype, regular"; + const char *methodNameSection = "__TEXT,__objc_methname,cstring_literals,no_dead_strip"; + const char *methodTypeSection = "__TEXT,__objc_methtype,regular,no_dead_strip"; - const char *classRefsSection = "__DATA,__objc_classrefs, regular"; - const char *protoRefsSection = "__DATA,__objc_protorefs, regular"; - const char *selectorRefsSection = "__DATA,__objc_selrefs, regular"; + const char *classRefsSection = "__DATA,__objc_classrefs,literal_pointers,no_dead_strip"; + const char *protoRefsSection = "__DATA,__objc_protorefs,literal_pointers,no_dead_strip"; + const char *selectorRefsSection = "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"; private: llvm::Module &module; @@ -175,8 +178,6 @@ class ObjCState { llvm::GlobalVariable *getEmptyCache(); - llvm::GlobalVariable *getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional=false); - llvm::GlobalVariable *getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type = nullptr); llvm::GlobalVariable *getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents); llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section = nullptr); From 2670dbb40f6aa181ef0898804c6e95ff6ce91f2c Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Wed, 20 Nov 2024 06:24:39 +0100 Subject: [PATCH 11/67] Fix objc link flag being added on non-darwin platforms --- driver/linker-gcc.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 26197b3d397..50f5252749e 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -715,7 +715,6 @@ void ArgsBuilder::addDefaultPlatformLibs() { // fallthrough case llvm::Triple::Darwin: case llvm::Triple::MacOSX: - this->addObjcStdlibLinkFlags(triple); case llvm::Triple::FreeBSD: case llvm::Triple::NetBSD: case llvm::Triple::OpenBSD: @@ -738,6 +737,13 @@ void ArgsBuilder::addDefaultPlatformLibs() { break; } + if (triple.isOSDarwin()) { + + // libobjc is more or less required, so we link against it here. + // This could be prettier, though. + this->addObjcStdlibLinkFlags(triple); + } + if (triple.isWindowsGNUEnvironment()) { // This is really more of a kludge, as linking in the Winsock functions // should be handled by the pragma(lib, ...) in std.socket, but it From 58e04b3e92b788039fb006e6fb690a61e2bf7cb9 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 21 Nov 2024 16:29:44 +0100 Subject: [PATCH 12/67] Refactor objc gen --- driver/linker-gcc.cpp | 2 +- gen/classes.cpp | 8 +- gen/llvm.h | 3 + gen/llvmhelpers.cpp | 2 +- gen/objcgen.cpp | 1007 ++++++++++++++++++----------------------- gen/objcgen.h | 555 ++++++++++++++++++----- gen/tocall.cpp | 10 +- gen/toir.cpp | 10 +- gen/tollvm.cpp | 95 ++++ gen/tollvm.h | 11 + 10 files changed, 999 insertions(+), 704 deletions(-) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 50f5252749e..251c7e58e0a 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -741,7 +741,7 @@ void ArgsBuilder::addDefaultPlatformLibs() { // libobjc is more or less required, so we link against it here. // This could be prettier, though. - this->addObjcStdlibLinkFlags(triple); + addObjcStdlibLinkFlags(triple); } if (triple.isWindowsGNUEnvironment()) { diff --git a/gen/classes.cpp b/gen/classes.cpp index 7895e0c9e62..b18362a10a2 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -357,13 +357,17 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { llvm::Function *kindOfClassFunc = getRuntimeFunction(loc, gIR->module, "objc_opt_isKindOfClass"); + llvm::Function *getClassFunc = + getRuntimeFunction(loc, gIR->module, "object_getClass"); + // Get the object. LLValue *obj = DtoRVal(val); + LLValue *objTy = gIR->CreateCallOrInvoke(getClassFunc, obj); // objc_opt_isKindOfClass will check if id is null // by itself, so we don't need to add an extra check. // objc_opt_isKindOfClass(id) ? id : null - LLValue *objCastable = gIR->CreateCallOrInvoke(kindOfClassFunc, obj); + LLValue *objCastable = gIR->CreateCallOrInvoke(kindOfClassFunc, obj, objTy); LLValue *ret = gIR->ir->CreateSelect(objCastable, obj, getNullPtr()); return new DImValue(_to, ret); } @@ -417,7 +421,7 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // Class && kindOfProtocolFunc(Class) ? id : null LLValue *ret = gIR->ir->CreateSelect( gIR->ir->CreateIsNotNull( - gIR->objc.unmaskPointer(objClass) // Classes may have metadata in their pointer, so remove it. + objClass ), gIR->ir->CreateSelect( gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass), diff --git a/gen/llvm.h b/gen/llvm.h index 6cd420340bc..af68ad4c925 100644 --- a/gen/llvm.h +++ b/gen/llvm.h @@ -60,3 +60,6 @@ using llvm::IRBuilder; #define LLConstantFP llvm::ConstantFP #define LLSmallVector llvm::SmallVector + +#define LLConstantList std::vector +#define LLStringRef llvm::StringRef \ No newline at end of file diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 798860003d4..cbdc789909c 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1843,7 +1843,7 @@ DLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, if (ad->classKind == ClassKind::objc) { auto tHandle = LLType::getInt64Ty(gIR->context()); - auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarOffset(*ad->isClassDeclaration(), *vd, false)); + auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarRef(ad->isClassDeclaration(), vd)->getOffset()); // Offset is now stored in tOffset. LLValue *ptr = src; diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index e8d90b3f71d..341987f65a8 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -38,83 +38,23 @@ bool objc_isSupported(const llvm::Triple &triple) { return false; } -llvm::GlobalVariable *ObjCState::getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type) { - if (type == nullptr) - type = getOpaquePtrType(); - - auto var = new LLGlobalVariable( - module, - type, - false, - LLGlobalValue::ExternalLinkage, - nullptr, - name, - nullptr, - LLGlobalVariable::NotThreadLocal, - 0, - true - ); - return var; -} - -LLGlobalVariable *ObjCState::getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents) { - if (packedContents.empty()) { - auto null_ = llvm::ConstantPointerNull::get( - LLPointerType::get(module.getContext(), 0) - ); - packedContents.push_back(null_); - } - - auto init = LLConstantStruct::getAnon( - packedContents, - true - ); - - auto var = new LLGlobalVariable( - module, - init->getType(), - false, - LLGlobalValue::ExternalLinkage, - init, - name, - nullptr, - LLGlobalVariable::NotThreadLocal, - 0, - false - ); - - var->setSection(dataSection); - return var; -} - -LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, - const llvm::StringRef &str, - const char *section) { - auto init = llvm::ConstantDataArray::getString(module.getContext(), str); - auto var = new LLGlobalVariable( - module, - init->getType(), - false, - LLGlobalValue::PrivateLinkage, - init, - symbol - ); - - if (section) - var->setSection(section); - return var; -} - -std::string ObjCState::getObjcTypeEncoding(Type *t) { +// TYPE ENCODINGS +std::string getTypeEncoding(Type *t) { std::string tmp; - switch (t->ty) { + case TY::Tclass: { + if (auto klass = t->isTypeClass()) { + return klass->sym->classKind == ClassKind::objc ? "@" : "?"; + } + return "?"; + } case TY::Tfunction: { - tmp = getObjcTypeEncoding(t->nextOf()); + tmp = getTypeEncoding(t->nextOf()); + tmp.append("@:"); if (auto func = t->isTypeFunction()) { for (size_t i = 0; i < func->parameterList.length(); i++) - tmp.append(getObjcTypeEncoding(func->parameterList[i]->type)); + tmp.append(getTypeEncoding(func->parameterList[i]->type)); } return tmp; } @@ -125,7 +65,7 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { return "*"; tmp.append("^"); - tmp.append(getObjcTypeEncoding(t->nextOf())); + tmp.append(getTypeEncoding(t->nextOf())); return tmp; } case TY::Tsarray: { @@ -136,7 +76,7 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { uinteger_t count = typ->dim->toUInteger(); tmp.append("["); tmp.append(std::to_string(count)); - tmp.append(getObjcTypeEncoding(typ->next)); + tmp.append(getTypeEncoding(typ->next)); tmp.append("]"); return tmp; } @@ -154,7 +94,7 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { tmp.append("="); for(unsigned int i = 0; i < sym->numArgTypes(); i++) { - tmp.append(getObjcTypeEncoding(sym->argType(i))); + tmp.append(getTypeEncoding(sym->argType(i))); } tmp.append(isUnion ? ")" : "}"); @@ -179,576 +119,558 @@ std::string ObjCState::getObjcTypeEncoding(Type *t) { case TY::Tcomplex64: return "jd"; case TY::Tfloat80: return "D"; case TY::Tcomplex80: return "jD"; - case TY::Tclass: { - if (auto klass = t->isTypeClass()) { - return klass->sym->classKind == ClassKind::objc ? "@" : "?"; - } - return "?"; - } default: return "?"; // unknown } } -// Helper functions to generate name symbols - // // STRING HELPERS // -std::string ObjCState::getObjcClassRoSymbol(const ClassDeclaration& cd, bool meta) { - return getObjcSymbolName(meta ? "OBJC_METACLASS_RO_$_" : "OBJC_CLASS_RO_$_", cd.ident->toChars()); +std::string getObjcClassRoSymbol(const char *name, bool meta) { + return getObjcSymbolName(meta ? "_OBJC_METACLASS_RO_$_" : "_OBJC_CLASS_RO_$_", name); } -std::string ObjCState::getObjcClassSymbol(const ClassDeclaration& cd, bool meta) { - return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", cd.ident->toChars()); +std::string getObjcClassSymbol(const char *name, bool meta) { + return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", name); } -std::string ObjCState::getObjcMethodListSymbol(const ClassDeclaration& cd, bool meta) { - return getObjcSymbolName(meta ? "OBJC_$_CLASS_METHODS_" : "OBJC_$_INSTANCE_METHODS_", cd.objc.identifier->toChars()); +std::string getObjcClassMethodListSymbol(const char *className, bool meta) { + return getObjcSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className); } -std::string ObjCState::getObjcProtoSymbol(const InterfaceDeclaration& id) { - return getObjcSymbolName("OBJC_PROTOCOL_$_", id.ident->toChars()); +std::string getObjcProtoMethodListSymbol(const char *className, bool meta) { + return getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className); } -std::string ObjCState::getObjcIvarSymbol(const ClassDeclaration& cd, const VarDeclaration& var) { - std::string tmp; - tmp.append("OBJC_IVAR_$_"); - tmp.append(cd.ident->toChars()); - tmp.append("."); - tmp.append(var.ident->toChars()); - return tmp; +std::string getObjcIvarListSymbol(const char *className) { + return getObjcSymbolName("_OBJC_$_INSTANCE_VARIABLES_", className); } -std::string ObjCState::getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { +std::string getObjcProtoSymbol(const char *name) { + return getObjcSymbolName("OBJC_PROTOCOL_$_", name); +} + +std::string getObjcIvarSymbol(const char *className, const char *varName) { + return ("OBJC_IVAR_$_" + std::string(className) + "." + std::string(varName)); +} + +std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { return (std::string(dsymPrefix) + std::string(dsymName)); } // -// UTILITIES +// METHODS // -LLGlobalVariable *ObjCState::getTypeEncoding(Type *t) { - return getMethodVarTypeName(getObjcTypeEncoding(t)); +LLConstant *ObjcMethod::emit() { + + // Extern declarations don't need to define + // a var type. + if (!decl->fbody) { + name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, true, true); + return selref; + } + + name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, true, true); + return selref; } -llvm::GlobalVariable* ObjCState::getEmptyCache() { - static llvm::GlobalVariable* g; - if(g == nullptr) - g = getGlobal(module, "_objc_empty_cache"); - return g; +// Implements the objc_method structure +LLConstant *ObjcMethod::info() { + if (!name) + emit(); + + if (!decl->fbody) + return nullptr; + + return LLConstantStruct::get( + getObjcMethodType(module), + { name, type, DtoCallee(decl) } + ); } -LLValue *ObjCState::unmaskPointer(LLValue *value) { - return gIR->ir->CreateAnd(value, OBJC_PTRMASK); +LLConstant *ObjcMethod::get() { + isUsed = true; + if (!name) + emit(); + + return selref; } // -// CLASSES +// INSTANCE VARIABLES // -unsigned int getClassFlags(const ClassDeclaration& cd) { - unsigned int flags = 0; - if (cd.objc.isRootClass()) - flags |= RO_ROOT; - - if (cd.objc.isMeta) - flags |= RO_META; +LLConstant *ObjcIvar::emit() { + name = makeGlobalStr(decl->ident->toChars(), OBJC_SECNAME_METHNAME, "OBJC_METH_VAR_NAME_"); + type = makeGlobalStr(getTypeEncoding(decl->type), OBJC_SECNAME_METHTYPE, "OBJC_METH_VAR_TYPE_"); + offset = makeGlobal(getObjcIvarSymbol(decl->parent->ident->toChars(), decl->ident->toChars()), nullptr, OBJC_SECNAME_IVAR); + offset->setInitializer(DtoConstUint(decl->offset)); + return nullptr; +} - return flags; +// Implements the objc_method structure +LLConstant *ObjcIvar::info() { + LLConstantList members; + + members.push_back(offset); + members.push_back(DtoConstCString(decl->ident->toChars())); + members.push_back(DtoConstCString(getTypeEncoding(decl->type).c_str())); + members.push_back(DtoConstUint(decl->alignment.value)); + members.push_back(DtoConstUint(decl->size(decl->loc))); + + return LLConstantStruct::get( + ObjcIvar::getObjcIvarType(module), + members + ); } -LLGlobalVariable *ObjCState::getClassSymbol(const ClassDeclaration& cd, bool meta) { - - auto csym = getObjcClassSymbol(cd, meta); - llvm::StringRef name(csym); - auto it = classSymbolTable.find(name); - if (it != classSymbolTable.end()) { - return it->second; - } - // Extern objects - if (cd.objc.isExtern) { - auto var = getGlobal(module, name); - classSymbolTable[name] = var; - retain(var); - return var; +// +// CLASS-LIKES +// + +void ObjcClasslike::onScan(bool meta) { + auto methods = + (meta && decl->objc.metaclass ) ? + decl->objc.metaclass->objc.methodList : + decl->objc.methodList; + + for(size_t i = 0; i < methods.length; i++) { + auto method = methods.ptr[i]; + + // Static functions are class methods. + if (method->isStatic()) { + auto mth = new ObjcMethod(module, method); + mth->get(); + classMethods.push_back(mth); + continue; + } + + auto mth = new ObjcMethod(module, method); + mth->get(); + instanceMethods.push_back(mth); } +} - // Classes and Metaclasses are the same thing in Objective-C - // - // Class symbol layout is as follows: - // struct objc_class { - // Class isa; // inherited from objc_object - // Class superclass; - // cache_t cache; // Formerly vtable and cache pointer - // class_data_bits_t bits; - // } - // - // the isa pointer points to the metaclass of the class. - // If the class is a metaclass, it points to the *root* metaclass. - // - // The superclass pointer will always will always point to the superclass - // of either the class or meta class. - ConstantList members; - if (meta) { - - // Find root meta-class. - const ClassDeclaration *metaDecl = &cd; - while(metaDecl->baseClass) - metaDecl = metaDecl->baseClass; - - members.push_back(getClassSymbol(*metaDecl, true)); - } else { +LLConstant *ObjcClasslike::emitMethodList(std::vector &methods) { + LLConstantList members; + + // Find out how many functions have actual bodies. + size_t count = 0; + for(size_t i = 0; i < methods.size(); i++) { + if (methods[i]->decl->fbody) + count++; + } - // Both register class and push metaclass on as the isa pointer. - classes.push_back(const_cast(&cd)); - members.push_back(getClassSymbol(cd, true)); + // Size of objc_method + members.push_back(DtoConstUint( + getTypeAllocSize( + ObjcMethod::getObjcMethodType(module)) + )); + + // Method count + members.push_back(DtoConstUint( + count + )); + + // Push on all the methods. + for(size_t i = 0; i < methods.size(); i++) { + if (methods[i]->decl->fbody) + members.push_back(methods[i]->info()); } - // Set the superclass field. - members.push_back( - wrapNull(cd.baseClass ? getClassSymbol(*cd.baseClass, meta) : nullptr) + return LLConstantStruct::getAnon( + members, + true ); +} - // cache_t and class_data_bits_t - members.push_back(getEmptyCache()); - members.push_back(getNullPtr()); +LLGlobalVariable *ObjcClasslike::emitName() { + if (className) + return className; + + className = makeGlobalStr(decl->objc.identifier->toChars(), "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); + return className; +} - // Attach Class read-only struct ref. - members.push_back( - wrapNull(getClassRoSymbol(cd, meta)) - ); +const char *ObjcClasslike::getName() { + return decl->objc.identifier->toChars(); +} + +// +// CLASSES +// + +size_t ObjcClass::getClassFlags(const ClassDeclaration& decl) { + size_t flags = 0; + if (!decl.baseClass) + flags |= RO_ROOT; + + if (decl.objc.isMeta) + flags |= RO_META; - // Cache it. - auto var = getGlobalWithBytes(module, name, members); - classSymbolTable[name] = var; - retain(var); - return var; + return flags; } -LLGlobalVariable *ObjCState::getClassRoName(const ClassDeclaration& cd) { - llvm::StringRef name(cd.ident->toChars()); - auto it = classRoNameTable.find(name); - if (it != classRoNameTable.end()) { - return it->second; +void ObjcClass::onScan(bool meta) { + ObjcClasslike::onScan(meta); + + if (!meta) { + + // Push on all the ivars. + for(size_t i = 0; i < decl->fields.size(); i++) { + ivars.push_back(new ObjcIvar(module, decl->fields[i])); + } + } +} + +LLConstant *ObjcClass::emitIvarList() { + LLConstantList members; + + // Size of ivar_t + members.push_back(DtoConstUint( + getTypeAllocSize(ObjcIvar::getObjcIvarType(module)) + )); + + // Ivar count + members.push_back(DtoConstUint( + ivars.size() + )); + + // Push on all the ivars. + for(size_t i = 0; i < ivars.size(); i++) { + members.push_back(ivars[i]->info()); } - auto var = getCStringVar("OBJC_CLASS_NAME", name, classNameSection); - classRoNameTable[name] = var; - retain(var); - return var; + return LLConstantStruct::getAnon( + members, + true + ); } -ptrdiff_t getInstanceStart(ClassDeclaration& cd, bool meta) { - if (meta) - return getPointerSizeInBits() == 64 ? - OBJC_METACLASS_INSTANCESTART_64 : OBJC_METACLASS_INSTANCESTART_32; +LLConstant *ObjcClass::emitProtocoList() { + LLConstantList members; + + // Size of ivar_t + members.push_back(DtoConstUint( + getTypeAllocSize(ObjcProtocol::getObjcProtocolType(module)) + )); + + // Ivar count + members.push_back(DtoConstUint( + ivars.size() + )); + + // Push on all the ivars. + for(size_t i = 0; i < ivars.size(); i++) { + members.push_back(ivars[i]->info()); + } + + return LLConstantStruct::getAnon( + members, + true + ); +} - ptrdiff_t start = cd.size(cd.loc); - if (!cd.members || cd.members->length == 0) +ptrdiff_t ObjcClass::getInstanceStart(bool meta) { + ptrdiff_t start = meta ? + getTypeAllocSize(ObjcClass::getObjcClassType(module)) : + getPointerSize(); + + // Meta-classes have no body. + if (meta || !decl->members || decl->members->length == 0) return start; - for(d_size_t idx = 0; idx < cd.members->length; idx++) + for(d_size_t idx = 0; idx < decl->members->length; idx++) { - auto var = ((*cd.members)[idx])->isVarDeclaration(); + auto var = (*decl->members)[idx]->isVarDeclaration(); if (var && var->isField()) - return var->offset; + return start+var->offset; } return start; } -size_t getInstanceSize(ClassDeclaration& cd, bool meta) { +size_t ObjcClass::getInstanceSize(bool meta) { + size_t start = meta ? + getTypeAllocSize(ObjcClass::getObjcClassType(module)) : + getPointerSize(); + if (meta) - return getPointerSizeInBits() == 64 ? - OBJC_METACLASS_INSTANCESTART_64 : OBJC_METACLASS_INSTANCESTART_32; + return start; - return cd.size(cd.loc); + return start+decl->size(decl->loc); } -LLGlobalVariable *ObjCState::getClassRoSymbol(const ClassDeclaration& cd, bool meta) { - auto name = getObjcClassRoSymbol(cd, meta); - auto it = classRoSymbolTable.find(name); - if (it != classRoSymbolTable.end()) { - return it->second; - } +LLValue *ObjcClass::getRefFor(LLValue *id) { + if (decl->objc.isExtern) { + if (decl->objc.isSwiftStub) { + auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); + return gIR->CreateCallOrInvoke(loadClassFunc, id, ""); + } - // Class read-only table layout is as follows: - // struct class_ro_t { - // uint32_t flags; - // uint32_t instanceStart; - // uint32_t instanceSize; - // uint32_t reserved; // Only on 64 bit platforms! - // const uint8_t * ivarLayout; - // const char * name; - // method_list_t * baseMethodList; - // protocol_list_t * baseProtocols; - // const ivar_list_t * ivars; - // const uint8_t * weakIvarLayout; - // property_list_t *baseProperties; - // }; - std::vector members; - members.push_back(DtoConstUint(getClassFlags(cd))); // flags - members.push_back(DtoConstUint(getInstanceStart(const_cast(cd), meta))); // instanceStart - members.push_back(DtoConstUint(getInstanceSize(const_cast(cd), meta))); // instanceSize - if (getPointerSizeInBits() == 64) - members.push_back(DtoConstUint(0)); // reserved - members.push_back(getNullPtr()); // ivarLayout - members.push_back(getClassRoName(cd)); // name - members.push_back(wrapNull(getMethodListFor(cd, meta, false))); // baseMethodList - members.push_back(wrapNull(getProtocolListFor(cd))); // baseProtocols - - if (meta) { - members.push_back(DtoConstUint(0)); // ivars - members.push_back(DtoConstUint(0)); // weakIvarLayout - members.push_back(DtoConstUint(0)); // baseProperties - } else { - auto ivarList = getIVarListFor(cd); - - members.push_back(ivarList); // ivars - members.push_back(DtoConstUint(0)); // weakIvarLayout - - // TODO: Implement Objective-C properties. - members.push_back(DtoConstUint(0)); // baseProperties + // We can't be sure that the isa "pointer" is actually a pointer to a class + // In extern scenarios, therefore we call object_getClass. + auto getClassFunc = getRuntimeFunction(decl->loc, module, "object_getClass"); + return gIR->CreateCallOrInvoke(getClassFunc, id, ""); } - - auto var = getGlobalWithBytes(module, name, members); - var->setSection(constSection); - classRoSymbolTable[name] = var; - retain(var); - return var; + // If we defined the type we can be 100% sure of the layout. + // so this is a fast path. + return classTable; } -LLValue *ObjCState::getSwiftStubClassReference(const ClassDeclaration& cd) { - auto classref = getClassReference(cd); - auto toClassRefFunc = getRuntimeFunction(cd.loc, module, "objc_loadClassRef"); - auto retv = gIR->CreateCallOrInvoke(toClassRefFunc, classref, ""); - return retv; +LLConstant *ObjcClass::getRootMetaClass() { + auto curr = decl; + while (curr->baseClass) + curr = curr->baseClass; + + auto name = getObjcClassSymbol(curr->objc.identifier->toChars(), true); + return getOrCreate( + name, + ObjcClass::getObjcClassType(module), + curr->objc.isExtern ? "" : OBJC_SECNAME_DATA + ); } -LLConstant *ObjCState::getClassReference(const ClassDeclaration& cd) { - llvm::StringRef name(cd.objc.identifier->toChars()); - auto it = classReferenceTable.find(name); - if (it != classReferenceTable.end()) { - return it->second; +LLConstant *ObjcClass::getSuper(bool meta) { + if (decl->objc.isRootClass() || !decl->baseClass) { + return meta ? classTable : metaClassTable; } - auto gvar = getClassSymbol(cd, false); - auto classref = new LLGlobalVariable( - module, gvar->getType(), - false, - LLGlobalValue::PrivateLinkage, gvar, "OBJC_CLASSLIST_REFERENCES_$_", nullptr, - LLGlobalVariable::NotThreadLocal, 0, - true - ); // externally initialized - - classref->setSection(classRefsSection); + auto super = decl->baseClass; + auto superName = getObjcClassSymbol(super->objc.identifier->toChars(), meta); + return getOrCreate( + superName, + ObjcClass::getObjcClassType(module), + super->objc.isExtern ? "" : OBJC_SECNAME_DATA + ); +} + +void ObjcClass::emitTable(LLGlobalVariable *table, LLConstant *meta, LLConstant *super, LLConstant *roTable) { + LLConstantList members; + members.push_back(meta); + members.push_back(super); + members.push_back(getEmptyCache()); + members.push_back(getNullPtr()); + members.push_back(roTable); - // Save for later lookup and prevent optimizer elimination - classReferenceTable[name] = classref; - retain(classref); - return classref; + table->setInitializer(LLConstantStruct::get( + getObjcClassType(module), + members + )); } -// -// CATEGORIES -// +void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { + LLConstantList members; + LLGlobalVariable *ivarList = nullptr; + LLGlobalVariable *protocolList = nullptr; + LLGlobalVariable *methodList = nullptr; + // Base Methods + auto baseMethods = meta ? + this->emitMethodList(classMethods) : + this->emitMethodList(instanceMethods); + methodList = getOrCreate(getObjcClassMethodListSymbol(getName(), meta), baseMethods->getType(), OBJC_SECNAME_CONST); + methodList->setInitializer(baseMethods); -// -// PROTOCOLS -// + if (!meta) { + // Base Protocols -LLGlobalVariable *ObjCState::getProtocolName(const llvm::StringRef &name) { - auto it = protocolNameTable.find(name); - if (it != protocolNameTable.end()) { - return it->second; + // Instance variables + auto baseIvars = emitIvarList(); + ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); + ivarList->setInitializer(baseIvars); } - auto var = getCStringVar("", name); - classRoNameTable[name] = var; - retain(var); - return var; + + // Build struct. + members.push_back(DtoConstUint(getClassFlags(meta ? *decl->objc.metaclass : *decl))); + members.push_back(DtoConstUint(getInstanceStart(meta))); + members.push_back(DtoConstUint(getInstanceSize(meta))); + members.push_back(getNullPtr()); + members.push_back(emitName()); + members.push_back(wrapNull(methodList)); + members.push_back(wrapNull(protocolList)); + members.push_back(wrapNull(ivarList)); + members.push_back(getNullPtr()); + members.push_back(getNullPtr()); //TODO: Add properties? + + table->setInitializer(LLConstantStruct::get( + getObjcClassRoType(module), + members + )); } -LLGlobalVariable *ObjCState::getProtocolListFor(const ClassDeclaration& cd) { - llvm::StringRef name(cd.objc.identifier->toChars()); - auto it = protocolListTable.find(name); - if (it != protocolListTable.end()) { - return it->second; +LLConstant *ObjcClass::emit() { + if (decl->objc.isSwiftStub && !decl->objc.isExtern) { + error(decl->loc, "Cannot define non-extern swift stub classes!"); + fatal(); } - // Protocol list layout is as follows: - // struct protocol_list_t { - // uintptr_t count; // count is 64-bit by accident. - // protocol_ref_t list[0]; // variable-size - // } - std::vector members; - members.push_back(DtoConstUlong(cd.interfaces.length)); - for(size_t i = 0; i < cd.interfaces.length; i++) { - auto iface = cd.interfaces.ptr[i]->sym->isInterfaceDeclaration(); - members.push_back(getProtocolReference(*iface)); - } + // Class already exists, just return that. + if (classTable) + return classTable; - // Cache and return. - auto var = getGlobalWithBytes(module, name, members); - protocolListTable[name] = var; - retain(var); - return var; -} + auto name = decl->objc.identifier->toChars(); + auto className = getObjcClassSymbol(name, false); + auto metaName = getObjcClassSymbol(name, true); -LLGlobalVariable *ObjCState::getProtocolSymbol(const InterfaceDeclaration& iface) { - llvm::StringRef name(iface.objc.identifier->toChars()); - auto it = protocolTable.find(name); - if (it != protocolTable.end()) { - return it->second; - } + // Extern classes only need non-ro refs. + if (decl->objc.isExtern) { + this->scan(); - // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L277 - // - // Protocol symbol layout is as follows: - // struct protocol_t { - // Class isa; // inherited from objc_object - // const char *mangledName; - // protocol_list_t *protocols; // This list is seperate from the module-level protocol list! - // method_list_t *instanceMethods; - // method_list_t *classMethods; - // method_list_t *optionalInstanceMethods; - // method_list_t *optionalClassMethods; - // property_list_t *instanceProperties; - // uint32_t size; // sizeof(protocol_t) - // uint32_t flags; - // } - std::vector members; - size_t protocolTSize = (getPointerSize()*9)+8; - - members.push_back(getNullPtr()); // unused? - members.push_back(getProtocolName(name)); // mangledName - members.push_back(getProtocolListFor(iface)); // protocols - members.push_back(getMethodListFor(iface, false)); // instanceMethods - members.push_back(getMethodListFor(iface, true)); // classMethods - members.push_back(getNullPtr()); // TODO: optionalInstanceMethods - members.push_back(getNullPtr()); // TODO: optionalClassMethods - members.push_back(getNullPtr()); // TODO: instanceProperties - members.push_back(DtoConstUint(protocolTSize)); - members.push_back(DtoConstUint(0)); // Should always be 0, other values are reserved for runtime. - - auto var = getGlobalWithBytes(module, name, members); - protocolTable[name] = var; - retain(var); - return var; -} - -LLGlobalVariable *ObjCState::getProtocolReference(const InterfaceDeclaration& iface) { - llvm::StringRef name(iface.objc.identifier->toChars()); - auto it = protocolReferenceTable.find(name); - if (it != protocolReferenceTable.end()) { - return it->second; + classTable = makeGlobal(className, ObjcClass::getObjcClassType(module), "", true, false); + metaClassTable = makeGlobal(metaName, ObjcClass::getObjcClassType(module), "", true, false); + return classTable; } - auto gvar = getProtocolSymbol(iface); - auto protoref = new LLGlobalVariable( - module, gvar->getType(), - false, // prevent const elimination optimization - LLGlobalValue::PrivateLinkage, gvar, "OBJC_PROTOLIST_REFERENCES_$_", nullptr, - LLGlobalVariable::NotThreadLocal, 0, - true - ); // externally initialized - - protoref->setSection(protoRefsSection); + auto classNameRo = getObjcClassRoSymbol(name, false); + auto metaNameRo = getObjcClassRoSymbol(name, true); - // Save for later lookup and prevent optimizer elimination - protocolReferenceTable[name] = protoref; - retain(protoref); - return protoref; + // If we were weakly declared before, go grab our declarations. + // Otherwise, create all the base tables for the type. + classTable = getOrCreate(className, ObjcClass::getObjcClassType(module), OBJC_SECNAME_DATA); + metaClassTable = getOrCreate(metaName, ObjcClass::getObjcClassType(module), OBJC_SECNAME_DATA); + classRoTable = getOrCreate(classNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); + metaClassRoTable = getOrCreate(metaNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); + + this->scan(); + + // Emit their structure. + this->emitName(); + this->emitTable(classTable, metaClassTable, getSuper(classTable), classRoTable); + this->emitTable(metaClassTable, getRootMetaClass(), getSuper(true), metaClassRoTable); + this->emitRoTable(classRoTable, false); + this->emitRoTable(metaClassRoTable, true); + return classTable; } -// -// INSTANCE VARIABLES -// -LLConstant *ObjCState::getIVarListFor(const ClassDeclaration& cd) { +LLConstant *ObjcClass::get() { + isUsed = true; - // If there's no fields, just return null. - if (cd.fields.empty()) { - return getNullPtr(); - } + if (!classTable) + return emit(); - auto name = getObjcSymbolName("OBJC_$_INSTANCE_VARIABLES_", cd.objc.identifier->toChars()); - auto it = ivarListTable.find(name); - if (it != ivarListTable.end()) { - return it->second; - } + return classTable; +} - ConstantList members; - members.push_back(DtoConstUint(OBJC_IVAR_ENTSIZE)); - members.push_back(DtoConstUint(cd.fields.length)); +// +// PROTOCOLS +// - for(size_t i = 0; i < cd.fields.length; i++) { - if (auto vd = cd.fields[i]->isVarDeclaration()) { - members.push_back(getIVarSymbol(cd, *vd)); - } - } +LLConstant *ObjcProtocol::emit() { - auto var = getGlobalWithBytes(module, name, members); - var->setSection(constSection); - ivarListTable[name] = var; - retain(var); - return var; -} - -LLGlobalVariable *ObjCState::getIVarSymbol(const ClassDeclaration& cd, const VarDeclaration& var) { - // struct ivar_t { - // int32_t *offset; - // const char *name; - // const char *type; - // // alignment is sometimes -1; use alignment() instead - // uint32_t alignment_raw; - // uint32_t size; - // } - auto name = getObjcIvarSymbol(cd, var); - auto it = ivarTable.find(name); - if (it != ivarTable.end()) { - return it->second; - } + // Protocol already exists, just return that. + if (protoref) + return protoref; - ConstantList members; - members.push_back(getIVarOffset(cd, var, false)); - members.push_back(getMethodVarName(var.ident->toChars())); - members.push_back(getMethodVarTypeName(getObjcTypeEncoding(var.type))); - members.push_back(DtoConstUint(var.alignment.isDefault() ? -1 : var.alignment.get())); - members.push_back(DtoConstUint(const_cast(var).size(var.loc))); - - auto retval = getGlobalWithBytes(module, name, members); - ivarTable[name] = retval; - retain(retval); - return retval; -} + auto name = decl->objc.identifier->toChars(); + auto protoName = getObjcProtoSymbol(name); -llvm::GlobalVariable *ObjCState::getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol) { - auto name = getObjcIvarSymbol(cd, var); - auto it = ivarOffsetTable.find(name); - if (it != ivarOffsetTable.end()) { - return it->second; + // Extern classes only need non-ro refs. + if (decl->objc.isExtern) { + this->scan(); + protocolTable = makeGlobal(protoName, nullptr, OBJC_SECNAME_DATA); + protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS); + return protoref; } - - LLGlobalVariable *retval; - if (cd.objc.isMeta) { - retval = getGlobal(module, name); - ivarOffsetTable[name] = retval; - retain(retval); - return retval; - } - ConstantList members; - members.push_back(DtoConstUlong(var.offset)); + // If we were weakly declared before, go grab our declarations. + // Otherwise, create all the base tables for the type. + protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); + protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS); - retval = getGlobalWithBytes(module, name, members); - ivarOffsetTable[name] = retval; - retain(retval); - return retval; + // Emit their structure. + this->scan(); + this->emitName(); + return protoref; } + // -// METHODS +// STATE // -LLGlobalVariable *ObjCState::getMethodVarName(const llvm::StringRef &name) { - auto it = methodNameTable.find(name); - if (it != methodNameTable.end()) { - return it->second; + +ObjcClass *ObjCState::getClassRef(ClassDeclaration *cd) { + for(auto it = classes.begin(); it != classes.end(); ++it) { + if (auto klass = *it) { + if (klass->decl == cd) { + return klass; + } + } } - auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, methodNameSection); - methodNameTable[name] = var; - retain(var); - return var; + auto klass = new ObjcClass(module, cd); + klass->get(); + classes.push_back(klass); + return klass; } -LLGlobalVariable *ObjCState::getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional) { - llvm::StringRef name(cd.objc.identifier->toChars()); - auto it = methodListTable.find(name); - if (it != methodListTable.end()) { - return it->second; +ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { + for(auto it = protocols.begin(); it != protocols.end(); ++it) { + if (auto proto = *it) { + if (proto->decl == id) { + return proto; + } + } } - auto methods = meta ? cd.objc.metaclass->objc.methodList : cd.objc.methodList; + auto proto = new ObjcProtocol(module, id); + proto->get(); + protocols.push_back(proto); + return proto; +} - // Count the amount of methods with a body. - size_t methodCount = 0; - for(size_t i = 0; i < methods.length; i++) { - if (methods.ptr[i]->fbody) methodCount++; +ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { + if (auto klass = getClassRef(cd)) { + klass->scan(); + return klass->getMethod(fd); } - // Empty classes don't need a method list generated. - if (!methodCount) - return nullptr; - - ConstantList members; + return nullptr; +} - // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L93 - members.push_back(DtoConstUint(OBJC_METHOD_SIZEOF)); - members.push_back(DtoConstUint(methodCount)); - for(size_t i = 0; i < methods.length; i++) { - if (methods.ptr[i]->fbody) { - auto selector = methods.ptr[i]->objc.selector; - - // See: https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.h#L207 - llvm::StringRef name(selector->stringvalue, selector->stringlen); - members.push_back(getMethodVarName(name)); - members.push_back(getMethodVarTypeName(name)); - members.push_back(DtoCallee(methods.ptr[i])); - } +ObjcMethod *ObjCState::getMethodRef(InterfaceDeclaration *id, FuncDeclaration *fd) { + if (auto proto = getProtocolRef(id)) { + proto->scan(); + return proto->getMethod(fd); } - auto var = getGlobalWithBytes(module, getObjcMethodListSymbol(cd, meta), members); - methodListTable[name] = var; - retain(var); - return var; + return nullptr; } -LLGlobalVariable *ObjCState::getMethodVarType(const FuncDeclaration& fd) { - return getTypeEncoding(fd.type); -} +ObjcMethod *ObjCState::getMethodRef(FuncDeclaration *fd) { + if (auto cd = fd->parent->isClassDeclaration()) { + if (auto func = getMethodRef(cd, fd)) { + return func; + } + } -LLGlobalVariable *ObjCState::getMethodVarTypeName(const llvm::StringRef& name) { - auto it = methodTypeTable.find(name); - if (it != methodTypeTable.end()) { - return it->second; + if (auto id = fd->parent->isInterfaceDeclaration()) { + if (auto func = getMethodRef(id, fd)) { + return func; + } } - auto var = getCStringVar("OBJC_METH_VAR_TYPE", name, methodTypeSection); - methodTypeTable[name] = var; - retain(var); - return var; + return nullptr; } -LLGlobalVariable *ObjCState::getSelector(const ObjcSelector &sel) { - llvm::StringRef name(sel.stringvalue, sel.stringlen); - auto it = selectorTable.find(name); - if (it != selectorTable.end()) { - return it->second; +ObjcIvar *ObjCState::getIVarRef(ClassDeclaration *cd, VarDeclaration *vd) { + if (auto klass = getClassRef(cd)) { + return klass->get(vd); } - auto gvar = getMethodVarName(name); - auto selref = new LLGlobalVariable( - module, gvar->getType(), - false, // prevent const elimination optimization - LLGlobalValue::PrivateLinkage, gvar, "OBJC_SELECTOR_REFERENCES_", nullptr, - LLGlobalVariable::NotThreadLocal, 0, - true - ); // externally initialized - - selref->setSection(selectorRefsSection); - - // Save for later lookup and prevent optimizer elimination - selectorTable[name] = selref; - retain(selref); - return selref; + return nullptr; } // @@ -759,47 +681,10 @@ void ObjCState::retain(LLConstant *sym) { retainedSymbols.push_back(sym); } -llvm::Constant *ObjCState::finalizeClasses() { - - // Objective-C needs to know which classes are in the output - // As such a protocol list needs to be generated. - std::vector members; - for(auto classRef = classes.begin(); classRef != classes.end(); ++classRef) { - auto klass = *classRef; - if (!klass->objc.isExtern && !klass->objc.isMeta) { - members.push_back(getClassSymbol(*klass, false)); - } - } - - auto var = getGlobalWithBytes(module, "L_OBJC_LABEL_CLASS_$", members); - var->setSection(classListSection); - return var; -} - -llvm::Constant *ObjCState::finalizeProtocols() { - - // Objective-C needs to know which protocols are in the output - // As such a protocol list needs to be generated. - std::vector members; - for(auto protoRef = protocols.begin(); protoRef != protocols.end(); ++protoRef) { - auto proto = *protoRef; - if (!proto->objc.isExtern) { - members.push_back(getProtocolSymbol(*proto)); - } - } - - auto var = getGlobalWithBytes(module, "L_OBJC_LABEL_PROTOCOL_$", members); - var->setSection(protoListSection); - return var; -} - void ObjCState::finalize() { - if (!retainedSymbols.empty()) { + genImageInfo(); - retainedSymbols.push_back(finalizeProtocols()); - retainedSymbols.push_back(finalizeClasses()); - - genImageInfo(); + if (!retainedSymbols.empty()) { // add in references so optimizer won't remove symbols. retainSymbols(); @@ -807,23 +692,21 @@ void ObjCState::finalize() { } void ObjCState::genImageInfo() { - // Use LLVM to generate image info - module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2); // Only support ABI 2. (Non-fragile) + module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2u); // Only support ABI 2. (Non-fragile) module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version - module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", - llvm::MDString::get(module.getContext(), imageInfoSection)); + module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", llvm::MDString::get(module.getContext(), OBJC_SECNAME_IMAGEINFO)); module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags } void ObjCState::retainSymbols() { - // put all objc symbols in the llvm.compiler.used array so optimizer won't - // remove. - auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), - retainedSymbols.size()); - auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); - auto var = new LLGlobalVariable(module, arrayType, false, - LLGlobalValue::AppendingLinkage, usedArray, - "llvm.compiler.used"); - var->setSection("llvm.metadata"); + // // put all objc symbols in the llvm.compiler.used array so optimizer won't + // // remove. + // auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), + // retainedSymbols.size()); + // auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); + // auto var = new LLGlobalVariable(module, arrayType, false, + // LLGlobalValue::AppendingLinkage, usedArray, + // "llvm.compiler.used"); + // var->setSection("llvm.metadata"); } diff --git a/gen/objcgen.h b/gen/objcgen.h index d5d7b3c6fed..8f2731890cc 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -17,6 +17,7 @@ #include "llvm/ADT/StringMap.h" #include "gen/tollvm.h" #include "dmd/mtype.h" +#include "dmd/errors.h" struct ObjcSelector; namespace llvm { @@ -35,158 +36,464 @@ class VarDeclaration; class Identifier; class Type; -typedef llvm::StringMap SymbolCache; -typedef std::vector ConstantList; - -bool objc_isSupported(const llvm::Triple &triple); - -// A mask needed to extract pointers from tagged Objective-C pointers. -// Only lower 35-bits in tagged pointers are used. -#define OBJC_PTRMASK 0x7ffffffff - -// TODO: maybe make this slightly less cursed by actually -// poking the Objective-C library? -#define OBJC_METHOD_SIZEOF 24 -#define OBJC_IVAR_ENTSIZE 32 -#define OBJC_METACLASS_INSTANCESTART_32 40 -#define OBJC_METACLASS_INSTANCESTART_64 76 - // class is a metaclass #define RO_META (1<<0) // class is a root class #define RO_ROOT (1<<1) -// Objective-C state tied to an LLVM module (object file). -class ObjCState { +// Section Names +#define OBJC_SECNAME_CLASSNAME "__TEXT,__objc_classname, cstring_literals" +#define OBJC_SECNAME_METHNAME "__TEXT,__objc_methname, cstring_literals" +#define OBJC_SECNAME_METHTYPE "__TEXT,__objc_methtype, cstring_literals" +#define OBJC_SECNAME_SELREFS "__DATA,__objc_selrefs, literal_pointers, no_dead_strip" +#define OBJC_SECNAME_IMAGEINFO "__DATA,__objc_imageinfo, regular, no_dead_strip" +#define OBJC_SECNAME_CLASSREFS "__DATA,__objc_classrefs, regular, no_dead_strip" +#define OBJC_SECNAME_CLASSLIST "__DATA,__objc_classlist, regular, no_dead_strip" +#define OBJC_SECNAME_STUBS "__DATA,__objc_stubs, regular, no_dead_strip" +#define OBJC_SECNAME_CATLIST "__DATA,__objc_catlist, regular, no_dead_strip" +#define OBJC_SECNAME_PROTOLIST "__DATA,__objc_protolist, regular, no_dead_strip" +#define OBJC_SECNAME_PROTOREFS "__DATA,__objc_protorefs, regular, no_dead_strip" +#define OBJC_SECNAME_CONST "__DATA,__objc_const" +#define OBJC_SECNAME_DATA "__DATA,__objc_data" +#define OBJC_SECNAME_IVAR "__DATA,__objc_ivar" + +// Names of Objective-C runtime structs +#define OBJC_STRUCTNAME_CLASSRO "class_ro_t" +#define OBJC_STRUCTNAME_CLASS "class_t" +#define OBJC_STRUCTNAME_STUBCLASS "stub_class_t" +#define OBJC_STRUCTNAME_PROTO "protocol_t" +#define OBJC_STRUCTNAME_METHOD "objc_method" + +// Gets the Objective-C type encoding for D type t +std::string getTypeEncoding(Type *t); + +// Gets whether Objective-C is supported. +bool objc_isSupported(const llvm::Triple &triple); + +// Generate name strings +std::string getObjcClassRoSymbol(const char *name, bool meta); +std::string getObjcClassSymbol(const char *name, bool meta); +std::string getObjcClassMethodListSymbol(const char *className, bool meta); +std::string getObjcIvarListSymbol(const char *className); +std::string getObjcIvarSymbol(const char *className, const char *varName); +std::string getObjcProtoMethodListSymbol(const char *className, bool meta); +std::string getObjcProtoSymbol(const char *name); +std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); + +template +using ObjcList = std::vector; + +// Base class for Objective-C definitions in a +// LLVM module. +class ObjcObject { public: - ObjCState(llvm::Module &module) : module(module) { } + ObjcObject(llvm::Module &module) : module(module) { } - // General - LLGlobalVariable *getTypeEncoding(Type *t); + // Whether the object is used. + bool isUsed; - // Classes - LLGlobalVariable *getClassSymbol(const ClassDeclaration& cd, bool meta); - LLGlobalVariable *getClassRoSymbol(const ClassDeclaration& cd, bool meta); - LLConstant *getClassReference(const ClassDeclaration& cd); - LLValue *getSwiftStubClassReference(const ClassDeclaration& cd); + // Gets a reference to the object in the module. + virtual LLConstant *get() { return nullptr; } - // Categories + // Gets the name of the object. + virtual const char *getName() { return nullptr; } - // Interface variables - LLConstant *getIVarListFor(const ClassDeclaration& cd); - LLGlobalVariable *getIVarSymbol(const ClassDeclaration& cd, const VarDeclaration& var); - LLGlobalVariable *getIVarOffset(const ClassDeclaration& cd, const VarDeclaration& var, bool outputSymbol); +protected: - // Methods - LLGlobalVariable *getMethodVarType(const FuncDeclaration& fd); - LLGlobalVariable *getMethodListFor(const ClassDeclaration& cd, bool meta, bool optional=false); + // Gets a global variable or creates it. + LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section) { + auto global = module.getGlobalVariable(name, true); + if (global) + return global; - // Selector - LLGlobalVariable *getSelector(const ObjcSelector &sel); + return makeGlobal(name, type, section, true, false); + } - // Protocols - LLGlobalVariable *getProtocolListFor(const ClassDeclaration& cd); - LLGlobalVariable *getProtocolSymbol(const InterfaceDeclaration& id); - LLGlobalVariable *getProtocolReference(const InterfaceDeclaration& id); + // The module the object resides in. + llvm::Module &module; - // Unmasks pointer to make sure it's not a tagged pointer. - LLValue *unmaskPointer(LLValue *value); + // Called to emit the data for the type. + virtual LLConstant *emit() { return nullptr; } +}; - void finalize(); +// objc_method +class ObjcMethod : public ObjcObject { +public: + FuncDeclaration *decl; + + ObjcMethod(llvm::Module &module, FuncDeclaration *decl) : ObjcObject(module), decl(decl) { } + + // Gets the main reference to the object. + LLConstant *get() override; + + // Gets the type of an Objective-C class_t struct + static LLStructType *getObjcMethodType(const llvm::Module& module) { + auto methodType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_METHOD); + if (methodType) + return methodType; + + methodType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // SEL name + getOpaquePtrType(), // const char *types + getOpaquePtrType(), // IMP imp + }, + OBJC_STRUCTNAME_METHOD + ); + return methodType; + } + + // Emits the constant struct containing the method + // information. + LLConstant *info(); + + // Gets the selector for the function + LLStringRef getSelector() { + auto selector = decl->objc.selector; + return LLStringRef(selector->stringvalue, selector->stringlen); + } + +protected: + + // Called to emit the object. + LLConstant *emit() override; + +private: + LLGlobalVariable *selref; + LLGlobalVariable *name; + LLGlobalVariable *type; +}; + +// ivar_t +class ObjcIvar : public ObjcObject { +public: + VarDeclaration *decl; + + ObjcIvar(llvm::Module &module, VarDeclaration *decl) :ObjcObject(module), decl(decl) { } + + // Gets the type for an Objective-C ivar_t struct. + static LLStructType *getObjcIvarType(const llvm::Module& module) { + auto ivarType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_STUBCLASS); + if (ivarType) + return ivarType; + + ivarType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // int32_t *offset + getOpaquePtrType(), // const char *name + getOpaquePtrType(), // const char *type + getI32Type(), // uint32_t alignment_raw + getI32Type(), // uint32_t size + }, + OBJC_STRUCTNAME_STUBCLASS + ); + return ivarType; + } + + LLConstant *getOffset() { + isUsed = true; + if (!name) + emit(); + + return offset; + } + + // Gets the main reference to the object. + LLConstant *get() override { + isUsed = true; + if (!name) + emit(); + + return name; + } + + // Emits the constant struct containing the method + // information. + LLConstant *info(); + +protected: + + // Called to emit the object. + LLConstant *emit() override; + +private: + LLGlobalVariable *offset; + LLGlobalVariable *name; + LLGlobalVariable *type; +}; - // Section Names - const char *imageInfoSection = "__DATA,__objc_imageinfo,regular,no_dead_strip"; +// Base type for class-like objective-c types. +class ObjcClasslike : public ObjcObject { +public: + ClassDeclaration *decl; + + ObjcClasslike(llvm::Module &module, ClassDeclaration *decl) : ObjcObject(module), decl(decl) { } + + const char *getName() override; + + virtual ObjcMethod *getMethod(FuncDeclaration *fd) { + for(auto it = instanceMethods.begin(); it != instanceMethods.end(); ++it) { + if (auto method = *it) { + if (method->decl == fd) { + return method; + } + } + } + + for(auto it = classMethods.begin(); it != classMethods.end(); ++it) { + if (auto method = *it) { + if (method->decl == fd) { + return method; + } + } + } + + return nullptr; + } + + // Scans the class-like object and fills out internal information + // about functions, ivars, etc. + void scan() { + if (!hasScanned) { + onScan(true); + onScan(false); + } + + hasScanned = true; + } + +protected: + LLGlobalVariable *emitName(); + + // Implement this to modify scanning behaviour. + virtual void onScan(bool meta); + + // Emits a method list as a constant. + LLConstant *emitMethodList(std::vector &methods); + + ObjcList instanceMethods; + ObjcList classMethods; +private: - // Lists - const char *classListSection = "__DATA,__objc_classlist,regular,no_dead_strip"; - const char *protoListSection = "__DATA,__objc_protolist,regular,no_dead_strip"; - const char *catListSection = "__DATA,__objc_catlist,regular,no_dead_strip"; + LLGlobalVariable *className; + bool hasScanned; +}; - const char *classNameSection = "__TEXT,__objc_classname,cstring_literals,no_dead_strip"; - const char *classStubsSection = "__DATA,__objc_stubs,regular,no_dead_strip"; +// objc_protocol_t +class ObjcProtocol : public ObjcClasslike { +public: + ObjcProtocol(llvm::Module &module, ClassDeclaration *decl) : ObjcClasslike(module, decl) { } + + // Gets the type of an Objective-C class_t struct + static LLStructType *getObjcProtocolType(const llvm::Module& module) { + auto protoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_PROTO); + if (protoType) + return protoType; + + protoType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // const char *mangledName + getOpaquePtrType(), // method_list_t* instanceMethods + getOpaquePtrType(), // method_list_t* classMethods + getOpaquePtrType(), // method_list_t* optionalInstanceMethods + getOpaquePtrType(), // method_list_t* optionalClassMethods + getOpaquePtrType(), // property_list_t* instanceProperties + getI32Type(), // uint32_t size + getI32Type(), // uint32_t flags + + // Further fields follow but are optional and are fixed up at + // runtime. + }, + OBJC_STRUCTNAME_PROTO + ); + return protoType; + } + + // Gets a protocol by its name + static LLGlobalVariable *get(const llvm::Module& module, LLStringRef name, bool meta = false) { + return module.getGlobalVariable(getObjcProtoSymbol(name.data()), true); + } + + // Gets the main reference to the object. + LLConstant *get() override { + isUsed = true; + if (!protocolTable) + emit(); + + return protocolTable; + } + +protected: + + // Called to emit the object. + LLConstant *emit() override; - const char *constSection = "__DATA,__objc_const,regular,no_dead_strip"; - const char *dataSection = "__DATA,__objc_data,regular,no_dead_strip"; +private: + LLGlobalVariable *protoref; + LLGlobalVariable *protocolTable; +}; - const char *methodNameSection = "__TEXT,__objc_methname,cstring_literals,no_dead_strip"; - const char *methodTypeSection = "__TEXT,__objc_methtype,regular,no_dead_strip"; +// class_t, class_ro_t, stub_class_t +class ObjcClass : public ObjcClasslike { +public: + ObjcClass(llvm::Module &module, ClassDeclaration *decl) : ObjcClasslike(module, decl) { } + + // Gets objective-c the flags for the class declaration + static size_t getClassFlags(const ClassDeclaration& decl); + + // Gets the type of an Objective-C class_t struct + static LLStructType *getObjcClassType(const llvm::Module& module) { + auto classType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASS); + if (classType) + return classType; + + classType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // objc_object* superclass + getOpaquePtrType(), // cache_t* cache + getOpaquePtrType(), // void* vtbl; (unused, set to null) + getOpaquePtrType(), // class_ro_t* ro + }, + OBJC_STRUCTNAME_CLASS + ); + return classType; + } + + // Gets the type of an Objective-C class_ro_t struct. + static LLStructType *getObjcClassRoType(const llvm::Module& module) { + auto classRoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASSRO); + if (classRoType) + return classRoType; + + classRoType = LLStructType::create( + module.getContext(), + { + getI32Type(), // uint32_t flags + getI32Type(), // uint32_t instanceStart + getI32Type(), // uint32_t instanceSize + getOpaquePtrType(), // void* layoutOrNonMetaClass + getOpaquePtrType(), // const char* name + getOpaquePtrType(), // method_list_t* baseMethods + getOpaquePtrType(), // protocol_list_t* baseProtocols + getOpaquePtrType(), // ivar_list_t* ivars + getOpaquePtrType(), // const uint8_t* weakIvarLayout + getOpaquePtrType(), // property_list_t* baseProperties + }, + OBJC_STRUCTNAME_CLASSRO + ); + return classRoType; + } + + // Gets the type for an Swift stub class + static LLStructType *getObjcStubClassType(const llvm::Module& module) { + auto stubClassType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_STUBCLASS); + if (stubClassType) + return stubClassType; + + stubClassType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // function pointer. + }, + OBJC_STRUCTNAME_STUBCLASS + ); + return stubClassType; + } + + // Gets a class by its name + static LLGlobalVariable *get(const llvm::Module& module, LLStringRef name, bool meta = false) { + return module.getGlobalVariable(getObjcClassSymbol(name.data(), meta), true); + } + + ObjcIvar *get(VarDeclaration *vd) { + for(size_t i = 0; i < ivars.size(); i++) { + if (ivars[i]->decl == vd) { + return ivars[i]; + } + } + + return nullptr; + } + + // Gets the main reference to the object. + LLConstant *get() override; + + // Gets the superclass of this class. + LLConstant *getSuper(bool meta); + + // Gets the root metaclass of this class. + LLConstant *getRootMetaClass(); + + // Gets the class reference for the specified id. + LLValue *getRefFor(LLValue *id); + +protected: + + // Called to emit the object. + LLConstant *emit() override; + + void onScan(bool meta) override; - const char *classRefsSection = "__DATA,__objc_classrefs,literal_pointers,no_dead_strip"; - const char *protoRefsSection = "__DATA,__objc_protorefs,literal_pointers,no_dead_strip"; - const char *selectorRefsSection = "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"; +private: + ObjcList ivars; + + // Core data + ptrdiff_t getInstanceStart(bool meta); + size_t getInstanceSize(bool meta); + + void emitTable(LLGlobalVariable *table, LLConstant *super, LLConstant *meta, LLConstant *roTable); + void emitRoTable(LLGlobalVariable *table, bool meta); + + // instance variables + LLConstant *emitIvarList(); + LLConstant *emitProtocoList(); + + // Gets the empty cache variable, and creates a reference to it + // if needed. + LLGlobalVariable *getEmptyCache() { + static LLGlobalVariable *objcCache; + if(!objcCache) + objcCache = makeGlobal("_objc_empty_cache", nullptr, "", true, false); + return objcCache; + } + + LLGlobalVariable *classTable; + LLGlobalVariable *classRoTable; + LLGlobalVariable *metaClassTable; + LLGlobalVariable *metaClassRoTable; +}; + +// Objective-C state tied to an LLVM module (object file). +class ObjCState { +public: + ObjCState(llvm::Module &module) : module(module) { } + + ObjcClass *getClassRef(ClassDeclaration *cd); + ObjcProtocol *getProtocolRef(InterfaceDeclaration *id); + ObjcMethod *getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd); + ObjcMethod *getMethodRef(InterfaceDeclaration *id, FuncDeclaration *fd); + ObjcMethod *getMethodRef(FuncDeclaration *fd); + ObjcIvar *getIVarRef(ClassDeclaration *cd, VarDeclaration *vd); + + void finalize(); private: llvm::Module &module; - // Symbols that shouldn't be optimized away - std::vector retainedSymbols; - - // Store the classes, protocols and new selectors. - std::vector classes; - std::vector protocols; - std::vector selectors; - - /// Cache for `__OBJC_METACLASS_$_`/`__OBJC_CLASS_$_` symbols. - SymbolCache classSymbolTable; - SymbolCache classRoSymbolTable; - SymbolCache classRoNameTable; - - /// Cache for `_OBJC_CLASS_$_` symbols stored in `__objc_stubs`. - /// NOTE: Stub classes have a different layout from normal classes - /// And need to be instantiated with a call to the objective-c runtime. - SymbolCache stubClassSymbolTable; - - /// Cache for `OBJC_CLASSLIST_REFERENCES_$_` symbols. - SymbolCache classReferenceTable; - - /// Cache for `OBJC_PROTOLIST_REFERENCES_$_` symbols. - SymbolCache protocolReferenceTable; - - /// Cache for `__OBJC_PROTOCOL_$_` symbols. - SymbolCache protocolTable; - SymbolCache protocolNameTable; - SymbolCache protocolListTable; - - // Cache for methods. - SymbolCache methodListTable; - SymbolCache methodNameTable; - SymbolCache methodTypeTable; - - // Cache for selectors. - SymbolCache selectorTable; - - // Cache for instance variables. - SymbolCache ivarTable; - SymbolCache ivarListTable; - SymbolCache ivarOffsetTable; - - // Generate name strings - std::string getObjcTypeEncoding(Type *t); - std::string getObjcClassRoSymbol(const ClassDeclaration& cd, bool meta); - std::string getObjcClassSymbol(const ClassDeclaration& cd, bool meta); - std::string getObjcMethodListSymbol(const ClassDeclaration& cd, bool meta); - std::string getObjcProtoSymbol(const InterfaceDeclaration& iface); - std::string getObjcIvarSymbol(const ClassDeclaration& cd, const VarDeclaration& var); - std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); - - // Generate name globals - llvm::GlobalVariable *getClassRoName(const ClassDeclaration& cd); - llvm::GlobalVariable *getProtocolName(const llvm::StringRef& name); - llvm::GlobalVariable *getMethodVarName(const llvm::StringRef& name); - llvm::GlobalVariable *getMethodVarTypeName(const llvm::StringRef& name); - - llvm::GlobalVariable *getEmptyCache(); - - llvm::GlobalVariable *getGlobal(llvm::Module& module, llvm::StringRef name, llvm::Type* type = nullptr); - llvm::GlobalVariable *getGlobalWithBytes(llvm::Module& module, llvm::StringRef name, ConstantList packedContents); - llvm::GlobalVariable *getCStringVar(const char *symbol, const llvm::StringRef &str, const char *section = nullptr); + ObjcList protocols; + ObjcList classes; + std::vector retainedSymbols; void retain(llvm::Constant *sym); - - void genImageInfo(); void retainSymbols(); - llvm::Constant *finalizeClasses(); - llvm::Constant *finalizeProtocols(); + void genImageInfo(); }; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 0b5b340ec3e..5f8823e58a0 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -743,7 +743,7 @@ class ImplicitArgumentsBuilder { // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( dfnval->vthis, - DtoLoad(getOpaquePtrType(), gIR->objc.getClassReference(*cls)), + DtoLoad(getOpaquePtrType(), gIR->objc.getClassRef(cls)->getRefFor(dfnval->vthis)), "super" ); @@ -792,11 +792,9 @@ class ImplicitArgumentsBuilder { if (irFty.arg_objcSelector) { assert(dfnval); - - const auto selector = dfnval->func->objc.selector; - assert(selector); - LLGlobalVariable *selptr = gIR->objc.getSelector(*selector); - args.push_back(DtoLoad(selptr->getValueType(), selptr)); + + auto selptr = gIR->objc.getMethodRef(dfnval->func)->get(); + args.push_back(DtoLoad(selptr->getType(), selptr)); } } diff --git a/gen/toir.cpp b/gen/toir.cpp index dc3822ee338..711173bfc49 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -337,18 +337,12 @@ class ToElemVisitor : public Visitor { // Protocols if (auto iface = e->classDeclaration->isInterfaceDeclaration()) { - auto loaded = DtoLoad(DtoType(e->type), gIR->objc.getProtocolReference(*iface)); + auto loaded = gIR->objc.getProtocolRef(iface)->get(); result = new DImValue(e->type, loaded); return; } - // Swift stub classes - if (e->classDeclaration->objc.isSwiftStub) { - result = new DImValue(e->type, gIR->objc.getSwiftStubClassReference(*e->classDeclaration)); - return; - } - - auto loaded = DtoLoad(DtoType(e->type), gIR->objc.getClassReference(*e->classDeclaration)); + auto loaded = gIR->objc.getClassRef(e->classDeclaration)->get(); result = new DImValue(e->type, loaded); } diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 2f6903ab96f..72b2b5c2e49 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -691,8 +691,103 @@ llvm::GlobalVariable *isaGlobalVar(LLValue *v) { //////////////////////////////////////////////////////////////////////////////// +LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type, LLStringRef section, bool extern_, bool externInit) { + if (!type) + type = getOpaquePtrType(); + + auto var = new LLGlobalVariable( + gIR->module, + type, + false, + extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage, + nullptr, + name, + nullptr, + LLGlobalValue::NotThreadLocal, + std::nullopt, + externInit + ); + + if (!section.empty()) + var->setSection(section); + + return var; +} + +LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedContents, LLStructType* type, bool extern_, bool externInit) { + if (packedContents.empty()) { + packedContents.push_back(getNullPtr()); + } + + // Handle initializer. + LLConstant *init; + if (type) { + init = LLConstantStruct::get( + type, + packedContents + ); + } else { + init = LLConstantStruct::getAnon( + packedContents, + true + ); + type = reinterpret_cast(init->getType()); + } + + auto var = new LLGlobalVariable( + gIR->module, + type, + false, + extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage, + init, + name, + nullptr, + LLGlobalValue::NotThreadLocal, + std::nullopt, + externInit + ); + + return var; +} + +LLGlobalVariable *makeGlobalRef(LLGlobalVariable *to, LLStringRef name, LLStringRef section, bool extern_, bool externInit) { + auto var = makeGlobal(name, to->getType(), section, extern_, externInit); + var->setInitializer(to); + return var; +} + +LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name, LLStringRef section, bool extern_, bool externInit) { + auto init = llvm::ConstantDataArray::getString(gIR->context(), text); + auto var = new LLGlobalVariable( + gIR->module, + init->getType(), + false, + extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage, + init, + name, + nullptr, + LLGlobalValue::NotThreadLocal, + std::nullopt, + externInit + ); + + if (!section.empty()) + var->setSection(section); + return var; +} + +//////////////////////////////////////////////////////////////////////////////// + LLType *getI8Type() { return LLType::getInt8Ty(gIR->context()); } +LLType *getI16Type() { return LLType::getInt16Ty(gIR->context()); } + +LLType *getI32Type() { return LLType::getInt32Ty(gIR->context()); } + +LLType *getI64Type() { return LLType::getInt64Ty(gIR->context()); } + +LLType *getSizeTType() { return LLType::getIntNTy(gIR->context(), getPointerSizeInBits()); } + LLPointerType *getOpaquePtrType(unsigned addressSpace) { return LLPointerType::get(gIR->context(), addressSpace); } diff --git a/gen/tollvm.h b/gen/tollvm.h index f4c298ca961..240c6ee2434 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -146,8 +146,19 @@ LLConstantInt *isaConstantInt(LLValue *v); llvm::Argument *isaArgument(LLValue *v); LLGlobalVariable *isaGlobalVar(LLValue *v); +// llvm::GlobalVariable wrappers for quickly making +// new global variables and references to them. +LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type = nullptr, LLStringRef section = "", bool extern_ = false, bool externInit = false); +LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedContents, LLStructType* type = nullptr, bool extern_ = false, bool externInit = false); +LLGlobalVariable *makeGlobalRef(LLGlobalVariable *to, LLStringRef name = "", LLStringRef section = "", bool extern_ = false, bool externInit = false); +LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name = "", LLStringRef section = "", bool extern_ = false, bool externInit = false); + // llvm::T::get(...) wrappers LLType *getI8Type(); +LLType *getI16Type(); +LLType *getI32Type(); +LLType *getI64Type(); +LLType *getSizeTType(); LLPointerType *getOpaquePtrType(unsigned addressSpace = 0); llvm::ConstantPointerNull *getNullPtr(); LLConstant *getNullValue(LLType *t); From da1cfbb565bf46c18b7a8846e606f8c48812ae1b Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 21 Nov 2024 16:39:43 +0100 Subject: [PATCH 13/67] remove use of std::nullopt --- gen/tollvm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 72b2b5c2e49..eeaeb219817 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -704,7 +704,7 @@ LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type, LLStringRef section name, nullptr, LLGlobalValue::NotThreadLocal, - std::nullopt, + 0u, externInit ); @@ -743,7 +743,7 @@ LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedCon name, nullptr, LLGlobalValue::NotThreadLocal, - std::nullopt, + 0u, externInit ); @@ -767,7 +767,7 @@ LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name, LLStringRef name, nullptr, LLGlobalValue::NotThreadLocal, - std::nullopt, + 0u, externInit ); From bcaff89d94f4333547d4d34004188818a1cd28f1 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 22 Nov 2024 06:31:51 +0100 Subject: [PATCH 14/67] Emit protocol tables --- gen/objcgen.cpp | 244 ++++++++++++++++++++++++++++++------------------ gen/objcgen.h | 29 +++++- gen/toir.cpp | 2 +- 3 files changed, 177 insertions(+), 98 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 341987f65a8..6bc3f4634a0 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -148,7 +148,11 @@ std::string getObjcIvarListSymbol(const char *className) { } std::string getObjcProtoSymbol(const char *name) { - return getObjcSymbolName("OBJC_PROTOCOL_$_", name); + return getObjcSymbolName("_OBJC_PROTOCOL_$_", name); +} + +std::string getObjcProtoListSymbol(const char *name, bool isProtocol) { + return getObjcSymbolName(isProtocol ? "_OBJC_PROTOCOL_PROTOCOLS_$_" : "_OBJC_CLASS_PROTOCOLS_$_", name); } std::string getObjcIvarSymbol(const char *className, const char *varName) { @@ -201,6 +205,35 @@ LLConstant *ObjcMethod::get() { return selref; } +LLConstant *ObjcObject::emitList(llvm::Module &module, LLType *elemType, LLConstantList objects, bool isCountPtrSized) { + LLConstantList members; + + // Size of stored struct. + size_t allocSize = getTypeAllocSize(elemType); + members.push_back( + isCountPtrSized ? + DtoConstSize_t(allocSize) : + DtoConstUint(allocSize) + ); + + // Method count + members.push_back(DtoConstUint( + objects.size() + )); + + // Insert all the objects in the constant list. + members.insert( + members.end(), + objects.begin(), + objects.end() + ); + + return LLConstantStruct::getAnon( + members, + true + ); +} + // // INSTANCE VARIABLES // @@ -258,36 +291,57 @@ void ObjcClasslike::onScan(bool meta) { } } -LLConstant *ObjcClasslike::emitMethodList(std::vector &methods) { - LLConstantList members; - - // Find out how many functions have actual bodies. - size_t count = 0; - for(size_t i = 0; i < methods.size(); i++) { - if (methods[i]->decl->fbody) - count++; +bool ifaceListHas(ObjcList &list, InterfaceDeclaration *curr) { + for(auto it = list.begin(); it != list.end(); ++it) { + if (*it == curr) + return true; } + return false; +} - // Size of objc_method - members.push_back(DtoConstUint( - getTypeAllocSize( - ObjcMethod::getObjcMethodType(module)) - )); +LLConstant *ObjcClasslike::emitProtocolList() { + LLConstantList list; - // Method count - members.push_back(DtoConstUint( - count - )); + auto ifaces = decl->interfaces; + for(size_t i = 0; i < ifaces.length; i++) { + if (auto iface = ifaces.ptr[i]) { + + // Only add interfaces which have objective-c linkage + // TODO: throw an error if you try to include a non-objective-c interface? + if (auto ifacesym = (InterfaceDeclaration *)iface->sym) { + if (ifacesym->classKind == ClassKind::objc) { + auto proto = getOrCreate( + getObjcProtoSymbol(ifacesym->ident->toChars()), + ObjcProtocol::getObjcProtocolType(module), "" + ); + + list.push_back(proto); + } + } + } + } - // Push on all the methods. + return ObjcObject::emitList( + module, + getOpaquePtrType(), + list + ); +} + +LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods) { + LLConstantList toAdd; + + // Find out how many functions have actual bodies. + size_t count = 0; for(size_t i = 0; i < methods.size(); i++) { if (methods[i]->decl->fbody) - members.push_back(methods[i]->info()); + toAdd.push_back(methods[i]->info()); } - return LLConstantStruct::getAnon( - members, - true + return ObjcObject::emitList( + module, + ObjcMethod::getObjcMethodType(module), + toAdd ); } @@ -354,30 +408,6 @@ LLConstant *ObjcClass::emitIvarList() { ); } -LLConstant *ObjcClass::emitProtocoList() { - LLConstantList members; - - // Size of ivar_t - members.push_back(DtoConstUint( - getTypeAllocSize(ObjcProtocol::getObjcProtocolType(module)) - )); - - // Ivar count - members.push_back(DtoConstUint( - ivars.size() - )); - - // Push on all the ivars. - for(size_t i = 0; i < ivars.size(); i++) { - members.push_back(ivars[i]->info()); - } - - return LLConstantStruct::getAnon( - members, - true - ); -} - ptrdiff_t ObjcClass::getInstanceStart(bool meta) { ptrdiff_t start = meta ? getTypeAllocSize(ObjcClass::getObjcClassType(module)) : @@ -408,24 +438,6 @@ size_t ObjcClass::getInstanceSize(bool meta) { return start+decl->size(decl->loc); } -LLValue *ObjcClass::getRefFor(LLValue *id) { - if (decl->objc.isExtern) { - if (decl->objc.isSwiftStub) { - auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); - return gIR->CreateCallOrInvoke(loadClassFunc, id, ""); - } - - // We can't be sure that the isa "pointer" is actually a pointer to a class - // In extern scenarios, therefore we call object_getClass. - auto getClassFunc = getRuntimeFunction(decl->loc, module, "object_getClass"); - return gIR->CreateCallOrInvoke(getClassFunc, id, ""); - } - - // If we defined the type we can be 100% sure of the layout. - // so this is a fast path. - return classTable; -} - LLConstant *ObjcClass::getRootMetaClass() { auto curr = decl; while (curr->baseClass) @@ -483,6 +495,9 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { if (!meta) { // Base Protocols + auto baseProtocols = emitProtocolList(); + protocolList = getOrCreate(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); + protocolList->setInitializer(baseProtocols); // Instance variables auto baseIvars = emitIvarList(); @@ -553,6 +568,34 @@ LLConstant *ObjcClass::emit() { return classTable; } +LLValue *ObjcClass::deref(LLValue *classptr) { + if (decl->objc.isExtern && decl->objc.isSwiftStub) { + auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); + return gIR->CreateCallOrInvoke(loadClassFunc, classptr, ""); + } + + return classptr; +} + +LLValue *ObjcClass::ref() { + return deref(classTable); +} + +LLValue *ObjcClass::getRefFor(LLValue *id) { + if (decl->objc.isExtern) { + + // We can't be sure that the isa "pointer" is actually a pointer to a class + // In extern scenarios, therefore we call object_getClass. + auto getClassFunc = getRuntimeFunction(decl->loc, module, "object_getClass"); + auto classref = gIR->CreateCallOrInvoke(getClassFunc, id, ""); + return deref(classref); + } + + // If we defined the type we can be 100% sure of the layout. + // so this is a fast path. + return deref(classTable); +} + LLConstant *ObjcClass::get() { isUsed = true; @@ -566,6 +609,42 @@ LLConstant *ObjcClass::get() { // PROTOCOLS // +void ObjcProtocol::emitTable(LLGlobalVariable *table) { + size_t allocSize = getTypeAllocSize(getObjcProtocolType(module)); + LLConstantList members; + + // Base Protocols + auto baseProtocols = this->emitProtocolList(); + auto protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); + protocolList->setInitializer(baseProtocols); + + // Class methods + auto classMethodConsts = this->emitMethodList(classMethods); + auto classMethodList = getOrCreate(getObjcProtoMethodListSymbol(getName(), true), classMethodConsts->getType(), OBJC_SECNAME_CONST); + classMethodList->setInitializer(classMethodConsts); + + // Instance methods + auto instanceMethodConsts = this->emitMethodList(instanceMethods); + auto instanceMethodList = getOrCreate(getObjcProtoMethodListSymbol(getName(), false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); + instanceMethodList->setInitializer(instanceMethodConsts); + + members.push_back(getNullPtr()); // isa + members.push_back(emitName()); // mangledName + members.push_back(protocolList); // protocol_list + members.push_back(instanceMethodList); // instanceMethods + members.push_back(classMethodList); // classMethods + members.push_back(getNullPtr()); // optionalInstanceMethods (TODO) + members.push_back(getNullPtr()); // optionalClassMethods (TODO) + members.push_back(getNullPtr()); // instanceProperties (TODO) + members.push_back(DtoConstUint(allocSize)); // size + members.push_back(DtoConstUint(0)); // flags + + table->setInitializer(LLConstantStruct::get( + getObjcProtocolType(module), + members + )); +} + LLConstant *ObjcProtocol::emit() { // Protocol already exists, just return that. @@ -578,20 +657,21 @@ LLConstant *ObjcProtocol::emit() { // Extern classes only need non-ro refs. if (decl->objc.isExtern) { this->scan(); - protocolTable = makeGlobal(protoName, nullptr, OBJC_SECNAME_DATA); - protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS); + protocolTable = makeGlobal(protoName, nullptr, "", true, false); + protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS, true, false); return protoref; } // If we were weakly declared before, go grab our declarations. // Otherwise, create all the base tables for the type. - protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); + protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), ""); protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS); // Emit their structure. this->scan(); this->emitName(); + this->emitTable(protocolTable); return protoref; } @@ -677,17 +757,10 @@ ObjcIvar *ObjCState::getIVarRef(ClassDeclaration *cd, VarDeclaration *vd) { // FINALIZATION // -void ObjCState::retain(LLConstant *sym) { - retainedSymbols.push_back(sym); -} - void ObjCState::finalize() { - genImageInfo(); - - if (!retainedSymbols.empty()) { - - // add in references so optimizer won't remove symbols. - retainSymbols(); + size_t totalObjects = classes.size()+protocols.size(); + if (totalObjects > 0) { + genImageInfo(); } } @@ -696,17 +769,4 @@ void ObjCState::genImageInfo() { module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", llvm::MDString::get(module.getContext(), OBJC_SECNAME_IMAGEINFO)); module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags -} - -void ObjCState::retainSymbols() { - - // // put all objc symbols in the llvm.compiler.used array so optimizer won't - // // remove. - // auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), - // retainedSymbols.size()); - // auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); - // auto var = new LLGlobalVariable(module, arrayType, false, - // LLGlobalValue::AppendingLinkage, usedArray, - // "llvm.compiler.used"); - // var->setSection("llvm.metadata"); -} +} \ No newline at end of file diff --git a/gen/objcgen.h b/gen/objcgen.h index 8f2731890cc..6d61ebba0dd 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -79,6 +79,7 @@ std::string getObjcIvarListSymbol(const char *className); std::string getObjcIvarSymbol(const char *className, const char *varName); std::string getObjcProtoMethodListSymbol(const char *className, bool meta); std::string getObjcProtoSymbol(const char *name); +std::string getObjcProtoListSymbol(const char *name); std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); template @@ -99,6 +100,9 @@ class ObjcObject { // Gets the name of the object. virtual const char *getName() { return nullptr; } + // Emits a new list for the specified objects as a constant. + static LLConstant *emitList(llvm::Module &module, LLType *elemType, LLConstantList objects, bool isCountPtrSized = false); + protected: // Gets a global variable or creates it. @@ -115,6 +119,14 @@ class ObjcObject { // Called to emit the data for the type. virtual LLConstant *emit() { return nullptr; } + + // Retains a symbol. + void retain(LLConstant *toRetain) { + retained.push_back(toRetain); + } + +private: + ObjcList retained; }; // objc_method @@ -274,6 +286,9 @@ class ObjcClasslike : public ObjcObject { // Emits a method list as a constant. LLConstant *emitMethodList(std::vector &methods); + // Emits a method list as a constant. + LLConstant *emitProtocolList(); + ObjcList instanceMethods; ObjcList classMethods; private: @@ -297,6 +312,7 @@ class ObjcProtocol : public ObjcClasslike { module.getContext(), { getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // protocol_list_t* protocols getOpaquePtrType(), // const char *mangledName getOpaquePtrType(), // method_list_t* instanceMethods getOpaquePtrType(), // method_list_t* classMethods @@ -333,6 +349,9 @@ class ObjcProtocol : public ObjcClasslike { // Called to emit the object. LLConstant *emit() override; + // Emits the protocol table. + void emitTable(LLGlobalVariable *table); + private: LLGlobalVariable *protoref; LLGlobalVariable *protocolTable; @@ -423,6 +442,9 @@ class ObjcClass : public ObjcClasslike { return nullptr; } + // Gets a reference to the class. + LLValue *ref(); + // Gets the main reference to the object. LLConstant *get() override; @@ -454,7 +476,8 @@ class ObjcClass : public ObjcClasslike { // instance variables LLConstant *emitIvarList(); - LLConstant *emitProtocoList(); + + LLValue *deref(LLValue *classptr); // Gets the empty cache variable, and creates a reference to it // if needed. @@ -491,9 +514,5 @@ class ObjCState { ObjcList protocols; ObjcList classes; - std::vector retainedSymbols; - void retain(llvm::Constant *sym); - void retainSymbols(); - void genImageInfo(); }; diff --git a/gen/toir.cpp b/gen/toir.cpp index 711173bfc49..05e4f5c1fe3 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -342,7 +342,7 @@ class ToElemVisitor : public Visitor { return; } - auto loaded = gIR->objc.getClassRef(e->classDeclaration)->get(); + auto loaded = gIR->objc.getClassRef(e->classDeclaration)->ref(); result = new DImValue(e->type, loaded); } From bc64a73b9b5a394c536afa4831238249076eb8db Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 22 Nov 2024 06:37:45 +0100 Subject: [PATCH 15/67] Remove unused variable --- gen/objcgen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 6bc3f4634a0..cb1c123580c 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -332,7 +332,6 @@ LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods) { LLConstantList toAdd; // Find out how many functions have actual bodies. - size_t count = 0; for(size_t i = 0; i < methods.size(); i++) { if (methods[i]->decl->fbody) toAdd.push_back(methods[i]->info()); From a28981a3d127ac2428afdbfd346fc20d531d324d Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 22 Nov 2024 07:22:33 +0100 Subject: [PATCH 16/67] Formatting --- runtime/druntime/src/core/attribute.d | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/runtime/druntime/src/core/attribute.d b/runtime/druntime/src/core/attribute.d index bd5f5d474ed..9f56908d7a2 100644 --- a/runtime/druntime/src/core/attribute.d +++ b/runtime/druntime/src/core/attribute.d @@ -40,6 +40,7 @@ version (D_ObjectiveC) { version = UdaOptional; version = UdaSelector; + version = UdaSwift; } version (Posix) @@ -50,6 +51,7 @@ version (CoreDdoc) version = UdaGNUAbiTag; version = UdaOptional; version = UdaSelector; + version = UdaSwift; } /** @@ -183,6 +185,16 @@ version (UdaSelector) struct selector version (UdaOptional) enum optional; + +/** + * Use this attribute to indicate that a Objective-C class is a Swift stub class. + * + * This is only allowed on classes, and classes marked as swift Objective-C classes + * cannot be subclassed. + */ +version (UdaSwift) + enum swift; + /** * Use this attribute to declare an ABI tag on a C++ symbol. * @@ -317,11 +329,3 @@ enum mustuse; * This is only allowed on `shared` static constructors, not thread-local module constructors. */ enum standalone; - -/** - * Use this attribute to indicate that a Objective-C class is a Swift stub class. - * - * This is only allowed on classes, and classes marked as swift Objective-C classes - * cannot be subclassed. - */ -enum swift; \ No newline at end of file From a7f6ef0e2145877d0fc727c5a2316918604a4290 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 22 Nov 2024 10:44:05 +0100 Subject: [PATCH 17/67] Fix build in release mode. Thanks for nothing, c++. --- gen/objcgen.cpp | 54 ++++++++++++++++++++++++------------------------- gen/objcgen.h | 29 +++++++++++--------------- gen/toir.cpp | 13 +++++++++--- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index cb1c123580c..c60cc0eeb73 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -278,16 +278,18 @@ void ObjcClasslike::onScan(bool meta) { auto method = methods.ptr[i]; // Static functions are class methods. - if (method->isStatic()) { - auto mth = new ObjcMethod(module, method); - mth->get(); - classMethods.push_back(mth); + if (meta && method->isStatic()) { + classMethods.push_back( + new ObjcMethod(module, method) + ); continue; } - auto mth = new ObjcMethod(module, method); - mth->get(); - instanceMethods.push_back(mth); + if (!meta && !method->isStatic()) { + instanceMethods.push_back( + new ObjcMethod(module, method) + ); + } } } @@ -678,28 +680,30 @@ LLConstant *ObjcProtocol::emit() { // // STATE // - - ObjcClass *ObjCState::getClassRef(ClassDeclaration *cd) { - for(auto it = classes.begin(); it != classes.end(); ++it) { - if (auto klass = *it) { - if (klass->decl == cd) { - return klass; + auto classList = this->classes; + + if (!classList.empty()) { + for(auto it : classList) { + if (it->decl == cd) { + return it; } } } auto klass = new ObjcClass(module, cd); - klass->get(); classes.push_back(klass); + klass->get(); return klass; } ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { - for(auto it = protocols.begin(); it != protocols.end(); ++it) { - if (auto proto = *it) { - if (proto->decl == id) { - return proto; + auto protoList = this->protocols; + + if (!protoList.empty()) { + for(auto it : protoList) { + if (it->decl == id) { + return it; } } } @@ -729,17 +733,11 @@ ObjcMethod *ObjCState::getMethodRef(InterfaceDeclaration *id, FuncDeclaration *f } ObjcMethod *ObjCState::getMethodRef(FuncDeclaration *fd) { - if (auto cd = fd->parent->isClassDeclaration()) { - if (auto func = getMethodRef(cd, fd)) { - return func; - } - } + if (auto cd = fd->parent->isClassDeclaration()) + return getMethodRef(cd, fd); - if (auto id = fd->parent->isInterfaceDeclaration()) { - if (auto func = getMethodRef(id, fd)) { - return func; - } - } + if (auto id = fd->parent->isInterfaceDeclaration()) + return getMethodRef(id, fd); return nullptr; } diff --git a/gen/objcgen.h b/gen/objcgen.h index 6d61ebba0dd..3e76b07b814 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -65,6 +65,8 @@ class Type; #define OBJC_STRUCTNAME_PROTO "protocol_t" #define OBJC_STRUCTNAME_METHOD "objc_method" +#define ObjcList std::vector + // Gets the Objective-C type encoding for D type t std::string getTypeEncoding(Type *t); @@ -82,9 +84,6 @@ std::string getObjcProtoSymbol(const char *name); std::string getObjcProtoListSymbol(const char *name); std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); -template -using ObjcList = std::vector; - // Base class for Objective-C definitions in a // LLVM module. class ObjcObject { @@ -247,19 +246,15 @@ class ObjcClasslike : public ObjcObject { const char *getName() override; virtual ObjcMethod *getMethod(FuncDeclaration *fd) { - for(auto it = instanceMethods.begin(); it != instanceMethods.end(); ++it) { - if (auto method = *it) { - if (method->decl == fd) { - return method; - } + for(auto it : instanceMethods) { + if (it->decl == fd) { + return it; } } - - for(auto it = classMethods.begin(); it != classMethods.end(); ++it) { - if (auto method = *it) { - if (method->decl == fd) { - return method; - } + + for(auto it : classMethods) { + if (it->decl == fd) { + return it; } } @@ -498,7 +493,7 @@ class ObjcClass : public ObjcClasslike { class ObjCState { public: ObjCState(llvm::Module &module) : module(module) { } - + ObjcClass *getClassRef(ClassDeclaration *cd); ObjcProtocol *getProtocolRef(InterfaceDeclaration *id); ObjcMethod *getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd); @@ -511,8 +506,8 @@ class ObjCState { private: llvm::Module &module; - ObjcList protocols; - ObjcList classes; + std::vector protocols; + std::vector classes; void genImageInfo(); }; diff --git a/gen/toir.cpp b/gen/toir.cpp index 05e4f5c1fe3..b08e990d0e2 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -337,13 +337,20 @@ class ToElemVisitor : public Visitor { // Protocols if (auto iface = e->classDeclaration->isInterfaceDeclaration()) { - auto loaded = gIR->objc.getProtocolRef(iface)->get(); + auto proto = gIR->objc.getProtocolRef(iface); + auto loaded = proto->get(); result = new DImValue(e->type, loaded); return; } - auto loaded = gIR->objc.getClassRef(e->classDeclaration)->ref(); - result = new DImValue(e->type, loaded); + // Classes + if (auto klass = gIR->objc.getClassRef(e->classDeclaration)) { + auto loaded = klass->ref(); + result = new DImValue(e->type, loaded); + return; + } + + llvm_unreachable("Unknown type for ObjcClassReferenceExp."); } ////////////////////////////////////////////////////////////////////////////// From 3895396731705fa07ae069e64557c4d78c941dd8 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 22 Nov 2024 10:59:57 +0100 Subject: [PATCH 18/67] Fix consistency --- gen/objcgen.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gen/objcgen.h b/gen/objcgen.h index 3e76b07b814..d89e6f45265 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -251,7 +251,7 @@ class ObjcClasslike : public ObjcObject { return it; } } - + for(auto it : classMethods) { if (it->decl == fd) { return it; @@ -506,8 +506,8 @@ class ObjCState { private: llvm::Module &module; - std::vector protocols; - std::vector classes; + ObjcList protocols; + ObjcList classes; void genImageInfo(); }; From 92bbe08f8aa1e1c177fefa2b98b8fa7dd9712388 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 22 Nov 2024 12:45:22 +0100 Subject: [PATCH 19/67] Fix dynamic casts --- gen/classes.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index b18362a10a2..73cd006a270 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -328,11 +328,12 @@ DValue *DtoCastClass(const Loc &loc, DValue *val, Type *_to) { } bool DtoIsObjcLinkage(Type *_to) { - TypeClass *to = static_cast(_to->toBasetype()); - auto sym = to->sym; - - DtoResolveClass(sym); - return sym->classKind == ClassKind::objc; + if (auto to = _to->isTypeClass()) { + DtoResolveClass(to->sym); + return to->sym->classKind == ClassKind::objc; + } + + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -357,12 +358,14 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { llvm::Function *kindOfClassFunc = getRuntimeFunction(loc, gIR->module, "objc_opt_isKindOfClass"); - llvm::Function *getClassFunc = - getRuntimeFunction(loc, gIR->module, "object_getClass"); - // Get the object. LLValue *obj = DtoRVal(val); - LLValue *objTy = gIR->CreateCallOrInvoke(getClassFunc, obj); + + // Get class_t handle + LLValue *objTy = getNullPtr(); + if (auto chndl = _to->isClassHandle()) { + objTy = gIR->objc.getClassRef(chndl)->ref(); + } // objc_opt_isKindOfClass will check if id is null // by itself, so we don't need to add an extra check. @@ -417,6 +420,12 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // id -> Class LLValue *obj = DtoRVal(val); LLValue *objClass = gIR->CreateCallOrInvoke(getClassFunc, obj); + + // Get prototype_t handle + LLValue *protoTy = getNullPtr(); + if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) { + protoTy = gIR->objc.getProtocolRef(ifhndl)->get(); + } // Class && kindOfProtocolFunc(Class) ? id : null LLValue *ret = gIR->ir->CreateSelect( @@ -424,7 +433,7 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { objClass ), gIR->ir->CreateSelect( - gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass), + gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass, protoTy), obj, getNullPtr() ), From 0fd521d21626af4a95db4f6c91140f082b6e9ca5 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sat, 23 Nov 2024 01:58:45 +0100 Subject: [PATCH 20/67] Fix tocall parentfd ref and arm msgsend call --- gen/abi/arm.cpp | 2 +- gen/tocall.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gen/abi/arm.cpp b/gen/abi/arm.cpp index 05cb77f81c5..34101c8b61a 100644 --- a/gen/abi/arm.cpp +++ b/gen/abi/arm.cpp @@ -128,7 +128,7 @@ struct ArmTargetABI : TargetABI { // see objc/message.h for objc_msgSend selection rules if (fty.arg_sret) { - return "objc_msgSend_stret"; + return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } return superCall ? "objc_msgSendSuper" : "objc_msgSend"; } diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 5f8823e58a0..bfdc7c0a1c0 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -737,7 +737,7 @@ class ImplicitArgumentsBuilder { if (objccall && directcall) { // ... or a Objective-c super call argument - if (auto parentfd = dfnval->func->parent->isFuncDeclaration()) { + if (auto parentfd = dfnval->func->isFuncDeclaration()) { if (auto cls = parentfd->parent->isClassDeclaration()) { // Create obj_super struct with (this, ) From 627db4f764820473550b17c5c5ae94719fb968ac Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sat, 23 Nov 2024 06:37:10 +0100 Subject: [PATCH 21/67] Make instance variables work --- gen/declarations.cpp | 10 ++- gen/llvmhelpers.cpp | 6 +- gen/objcgen.cpp | 163 +++++++++++++++++++++++++++++++++++-------- gen/objcgen.h | 51 +++++++++----- gen/tocall.cpp | 7 +- ir/irclass.cpp | 5 ++ ir/irtypeaggr.cpp | 9 ++- ir/irtypeclass.cpp | 8 +++ 8 files changed, 202 insertions(+), 57 deletions(-) diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 9b66d9a2bab..ed8902f70c3 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -110,8 +110,10 @@ class CodegenVisitor : public Visitor { } // Objective-C protocols don't have TypeInfo. - if(decl->classKind == ClassKind::objc) + if (decl->classKind == ClassKind::objc) { + gIR->objc.emit(decl); return; + } // Emit TypeInfo. IrClass *ir = getIrAggr(decl); @@ -209,9 +211,11 @@ class CodegenVisitor : public Visitor { m->accept(this); } - // TODO: Support creating objective-c exposed classes? - if (decl->classKind == ClassKind::objc) + // Objective-C class structure is initialized by calling getClassRef. + if (decl->classKind == ClassKind::objc) { + gIR->objc.emit(decl); return; + } IrClass *ir = getIrAggr(decl); diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index cbdc789909c..767752f1ec1 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1842,13 +1842,13 @@ DLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, DtoResolveDsymbol(ad); if (ad->classKind == ClassKind::objc) { - auto tHandle = LLType::getInt64Ty(gIR->context()); - auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarRef(ad->isClassDeclaration(), vd)->getOffset()); + auto tHandle = getI32Type(); + auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarOffset(ad->isClassDeclaration(), vd)); // Offset is now stored in tOffset. LLValue *ptr = src; ptr = DtoBitCast(ptr, getOpaquePtrType()); - ptr = DtoGEP1(llvm::Type::getInt8Ty(gIR->context()), ptr, tOffset); + ptr = DtoGEP1(getI8Type(), ptr, tOffset); return new DLValue(vd->type, ptr); } diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index c60cc0eeb73..89f6b2e0aa9 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -163,6 +163,19 @@ std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { return (std::string(dsymPrefix) + std::string(dsymName)); } +// +// ObjcObject +// +void ObjcObject::retain(LLGlobalVariable *toRetain) { + for(auto elem : objc.retained) { + if (elem == toRetain) + return; + } + + objc.retained.push_back(toRetain); +} + + // // METHODS // @@ -239,21 +252,36 @@ LLConstant *ObjcObject::emitList(llvm::Module &module, LLType *elemType, LLConst // LLConstant *ObjcIvar::emit() { - name = makeGlobalStr(decl->ident->toChars(), OBJC_SECNAME_METHNAME, "OBJC_METH_VAR_NAME_"); - type = makeGlobalStr(getTypeEncoding(decl->type), OBJC_SECNAME_METHTYPE, "OBJC_METH_VAR_TYPE_"); - offset = makeGlobal(getObjcIvarSymbol(decl->parent->ident->toChars(), decl->ident->toChars()), nullptr, OBJC_SECNAME_IVAR); - offset->setInitializer(DtoConstUint(decl->offset)); + auto ivarsym = getObjcIvarSymbol(decl->parent->ident->toChars(), decl->ident->toChars()); + + // Extern, emit data. + if (auto klass = decl->parent->isClassDeclaration()) { + if (klass->objc.isExtern) { + name = makeGlobal(ivarsym, nullptr, "OBJC_METH_VAR_NAME_", true, true); + type = makeGlobal(ivarsym, nullptr, "OBJC_METH_VAR_TYPE_", true, true); + offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + return nullptr; + } + + // Non-extern, emit data. + name = makeGlobalStr(decl->ident->toChars(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + + offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + offset->setInitializer(DtoConstUint(decl->offset)); + } return nullptr; } // Implements the objc_method structure LLConstant *ObjcIvar::info() { LLConstantList members; + this->get(); members.push_back(offset); - members.push_back(DtoConstCString(decl->ident->toChars())); - members.push_back(DtoConstCString(getTypeEncoding(decl->type).c_str())); - members.push_back(DtoConstUint(decl->alignment.value)); + members.push_back(name); + members.push_back(type); + members.push_back(DtoConstUint(decl->alignment.isDefault() ? -1 : decl->alignment.get())); members.push_back(DtoConstUint(decl->size(decl->loc))); return LLConstantStruct::get( @@ -262,8 +290,6 @@ LLConstant *ObjcIvar::info() { ); } - - // // CLASS-LIKES // @@ -280,14 +306,14 @@ void ObjcClasslike::onScan(bool meta) { // Static functions are class methods. if (meta && method->isStatic()) { classMethods.push_back( - new ObjcMethod(module, method) + new ObjcMethod(module, objc, method) ); continue; } if (!meta && !method->isStatic()) { instanceMethods.push_back( - new ObjcMethod(module, method) + new ObjcMethod(module, objc, method) ); } } @@ -362,6 +388,15 @@ const char *ObjcClasslike::getName() { // CLASSES // +LLGlobalVariable *ObjcClass::getIVarOffset(VarDeclaration *vd) { + auto ivarsym = getObjcIvarSymbol(decl->ident->toChars(), vd->ident->toChars()); + auto ivoffset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + ivoffset->setInitializer(DtoConstUint(vd->offset)); + this->retain(ivoffset); + + return ivoffset; +} + size_t ObjcClass::getClassFlags(const ClassDeclaration& decl) { size_t flags = 0; if (!decl.baseClass) @@ -377,10 +412,12 @@ void ObjcClass::onScan(bool meta) { ObjcClasslike::onScan(meta); if (!meta) { - + // Push on all the ivars. for(size_t i = 0; i < decl->fields.size(); i++) { - ivars.push_back(new ObjcIvar(module, decl->fields[i])); + auto ivar = new ObjcIvar(module, objc, decl->fields[i]); + ivars.push_back(ivar); + ivar->get(); } } } @@ -531,6 +568,8 @@ LLConstant *ObjcClass::emit() { fatal(); } + assert(decl && !decl->isInterfaceDeclaration()); + // Class already exists, just return that. if (classTable) return classTable; @@ -545,6 +584,13 @@ LLConstant *ObjcClass::emit() { classTable = makeGlobal(className, ObjcClass::getObjcClassType(module), "", true, false); metaClassTable = makeGlobal(metaName, ObjcClass::getObjcClassType(module), "", true, false); + + // Still emit ivars. + auto baseIvars = emitIvarList(); + auto ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); + ivarList->setInitializer(baseIvars); + this->retain(ivarList); + return classTable; } @@ -658,7 +704,8 @@ LLConstant *ObjcProtocol::emit() { // Extern classes only need non-ro refs. if (decl->objc.isExtern) { this->scan(); - protocolTable = makeGlobal(protoName, nullptr, "", true, false); + + protocolTable = makeGlobal(protoName, nullptr, OBJC_SECNAME_DATA, true, false); protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS, true, false); return protoref; } @@ -666,13 +713,17 @@ LLConstant *ObjcProtocol::emit() { // If we were weakly declared before, go grab our declarations. // Otherwise, create all the base tables for the type. - protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), ""); + protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS); + // Emit their structure. this->scan(); this->emitName(); this->emitTable(protocolTable); + + this->retain(protocolTable); + this->retain(protoref); return protoref; } @@ -681,8 +732,10 @@ LLConstant *ObjcProtocol::emit() { // STATE // ObjcClass *ObjCState::getClassRef(ClassDeclaration *cd) { - auto classList = this->classes; + if (auto id = cd->isInterfaceDeclaration()) + return nullptr; + auto classList = this->classes; if (!classList.empty()) { for(auto it : classList) { if (it->decl == cd) { @@ -691,15 +744,15 @@ ObjcClass *ObjCState::getClassRef(ClassDeclaration *cd) { } } - auto klass = new ObjcClass(module, cd); + auto klass = new ObjcClass(module, *this, cd); classes.push_back(klass); klass->get(); return klass; } ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { - auto protoList = this->protocols; + auto protoList = this->protocols; if (!protoList.empty()) { for(auto it : protoList) { if (it->decl == id) { @@ -708,25 +761,38 @@ ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { } } - auto proto = new ObjcProtocol(module, id); - proto->get(); + auto proto = new ObjcProtocol(module, *this, id); protocols.push_back(proto); + proto->get(); return proto; } ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { + if (auto id = cd->isInterfaceDeclaration()) { + if (auto proto = getProtocolRef(id)) { + proto->scan(); + + // Attempt to get the method, if not found + // try the parent. + auto method = proto->getMethod(fd); + if (!method && id->baseClass) { + method = getMethodRef(id->baseClass, fd); + } + return proto->getMethod(fd); + } + } + if (auto klass = getClassRef(cd)) { klass->scan(); - return klass->getMethod(fd); - } - return nullptr; -} + // Attempt to get the method, if not found + // try the parent. + auto method = klass->getMethod(fd); + if (!method && cd->baseClass) { + method = getMethodRef(cd->baseClass, fd); + } -ObjcMethod *ObjCState::getMethodRef(InterfaceDeclaration *id, FuncDeclaration *fd) { - if (auto proto = getProtocolRef(id)) { - proto->scan(); - return proto->getMethod(fd); + return method; } return nullptr; @@ -750,14 +816,39 @@ ObjcIvar *ObjCState::getIVarRef(ClassDeclaration *cd, VarDeclaration *vd) { return nullptr; } +LLGlobalVariable *ObjCState::getIVarOffset(ClassDeclaration *cd, VarDeclaration *vd) { + if (auto klass = getClassRef(cd)) { + return klass->getIVarOffset(vd); + } + + return nullptr; +} + +void ObjCState::emit(ClassDeclaration *cd) { + + // Meta-classes are emitted automatically with the class, + // as such we only need to emit a classref for the base class. + if (!cd || cd->objc.isMeta) + return; + + if (auto id = cd->isInterfaceDeclaration()) { + getProtocolRef(id); + return; + } + + getClassRef(cd); +} + // // FINALIZATION // void ObjCState::finalize() { - size_t totalObjects = classes.size()+protocols.size(); + size_t totalObjects = classes.size()+protocols.size()+retained.size(); if (totalObjects > 0) { + genImageInfo(); + retainSymbols(); } } @@ -766,4 +857,18 @@ void ObjCState::genImageInfo() { module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", llvm::MDString::get(module.getContext(), OBJC_SECNAME_IMAGEINFO)); module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags +} + +void ObjCState::retainSymbols() { + auto retainedSymbols = this->retained; + + if (!retainedSymbols.empty()) { + auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), + retainedSymbols.size()); + auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); + auto var = new LLGlobalVariable(module, arrayType, false, + LLGlobalValue::AppendingLinkage, usedArray, + "llvm.compiler.used"); + var->setSection("llvm.metadata"); + } } \ No newline at end of file diff --git a/gen/objcgen.h b/gen/objcgen.h index d89e6f45265..92ffd9fff3e 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -36,6 +36,9 @@ class VarDeclaration; class Identifier; class Type; +// Fwd declaration. +class ObjCState; + // class is a metaclass #define RO_META (1<<0) @@ -53,7 +56,7 @@ class Type; #define OBJC_SECNAME_STUBS "__DATA,__objc_stubs, regular, no_dead_strip" #define OBJC_SECNAME_CATLIST "__DATA,__objc_catlist, regular, no_dead_strip" #define OBJC_SECNAME_PROTOLIST "__DATA,__objc_protolist, regular, no_dead_strip" -#define OBJC_SECNAME_PROTOREFS "__DATA,__objc_protorefs, regular, no_dead_strip" +#define OBJC_SECNAME_PROTOREFS "__DATA,__objc_protorefs, regular" #define OBJC_SECNAME_CONST "__DATA,__objc_const" #define OBJC_SECNAME_DATA "__DATA,__objc_data" #define OBJC_SECNAME_IVAR "__DATA,__objc_ivar" @@ -63,6 +66,7 @@ class Type; #define OBJC_STRUCTNAME_CLASS "class_t" #define OBJC_STRUCTNAME_STUBCLASS "stub_class_t" #define OBJC_STRUCTNAME_PROTO "protocol_t" +#define OBJC_STRUCTNAME_IVAR "ivar_t" #define OBJC_STRUCTNAME_METHOD "objc_method" #define ObjcList std::vector @@ -84,11 +88,12 @@ std::string getObjcProtoSymbol(const char *name); std::string getObjcProtoListSymbol(const char *name); std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); + // Base class for Objective-C definitions in a // LLVM module. class ObjcObject { public: - ObjcObject(llvm::Module &module) : module(module) { } + ObjcObject(llvm::Module &module, ObjCState &objc) : module(module), objc(objc) { } // Whether the object is used. bool isUsed; @@ -105,27 +110,25 @@ class ObjcObject { protected: // Gets a global variable or creates it. - LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section) { + LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false) { auto global = module.getGlobalVariable(name, true); if (global) return global; - return makeGlobal(name, type, section, true, false); + return makeGlobal(name, type, section, true, extInitializer); } // The module the object resides in. llvm::Module &module; + // The irstate. + ObjCState &objc; + // Called to emit the data for the type. virtual LLConstant *emit() { return nullptr; } // Retains a symbol. - void retain(LLConstant *toRetain) { - retained.push_back(toRetain); - } - -private: - ObjcList retained; + void retain(LLGlobalVariable *toRetain); }; // objc_method @@ -133,7 +136,8 @@ class ObjcMethod : public ObjcObject { public: FuncDeclaration *decl; - ObjcMethod(llvm::Module &module, FuncDeclaration *decl) : ObjcObject(module), decl(decl) { } + ObjcMethod(llvm::Module &module, ObjCState &objc, FuncDeclaration *decl) : + ObjcObject(module, objc), decl(decl) { } // Gets the main reference to the object. LLConstant *get() override; @@ -182,11 +186,12 @@ class ObjcIvar : public ObjcObject { public: VarDeclaration *decl; - ObjcIvar(llvm::Module &module, VarDeclaration *decl) :ObjcObject(module), decl(decl) { } + ObjcIvar(llvm::Module &module, ObjCState &objc, VarDeclaration *decl) : + ObjcObject(module, objc), decl(decl) { } // Gets the type for an Objective-C ivar_t struct. static LLStructType *getObjcIvarType(const llvm::Module& module) { - auto ivarType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_STUBCLASS); + auto ivarType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_IVAR); if (ivarType) return ivarType; @@ -199,7 +204,7 @@ class ObjcIvar : public ObjcObject { getI32Type(), // uint32_t alignment_raw getI32Type(), // uint32_t size }, - OBJC_STRUCTNAME_STUBCLASS + OBJC_STRUCTNAME_IVAR ); return ivarType; } @@ -241,7 +246,8 @@ class ObjcClasslike : public ObjcObject { public: ClassDeclaration *decl; - ObjcClasslike(llvm::Module &module, ClassDeclaration *decl) : ObjcObject(module), decl(decl) { } + ObjcClasslike(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : + ObjcObject(module, objc), decl(decl) { } const char *getName() override; @@ -295,7 +301,8 @@ class ObjcClasslike : public ObjcObject { // objc_protocol_t class ObjcProtocol : public ObjcClasslike { public: - ObjcProtocol(llvm::Module &module, ClassDeclaration *decl) : ObjcClasslike(module, decl) { } + ObjcProtocol(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : + ObjcClasslike(module, objc, decl) { } // Gets the type of an Objective-C class_t struct static LLStructType *getObjcProtocolType(const llvm::Module& module) { @@ -355,7 +362,8 @@ class ObjcProtocol : public ObjcClasslike { // class_t, class_ro_t, stub_class_t class ObjcClass : public ObjcClasslike { public: - ObjcClass(llvm::Module &module, ClassDeclaration *decl) : ObjcClasslike(module, decl) { } + ObjcClass(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : + ObjcClasslike(module, objc, decl) { } // Gets objective-c the flags for the class declaration static size_t getClassFlags(const ClassDeclaration& decl); @@ -437,6 +445,8 @@ class ObjcClass : public ObjcClasslike { return nullptr; } + LLGlobalVariable *getIVarOffset(VarDeclaration *vd); + // Gets a reference to the class. LLValue *ref(); @@ -491,23 +501,28 @@ class ObjcClass : public ObjcClasslike { // Objective-C state tied to an LLVM module (object file). class ObjCState { +friend ObjcObject; public: + ObjCState(llvm::Module &module) : module(module) { } + void emit(ClassDeclaration *cd); ObjcClass *getClassRef(ClassDeclaration *cd); ObjcProtocol *getProtocolRef(InterfaceDeclaration *id); ObjcMethod *getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd); - ObjcMethod *getMethodRef(InterfaceDeclaration *id, FuncDeclaration *fd); ObjcMethod *getMethodRef(FuncDeclaration *fd); ObjcIvar *getIVarRef(ClassDeclaration *cd, VarDeclaration *vd); + LLGlobalVariable *getIVarOffset(ClassDeclaration *cd, VarDeclaration *vd); void finalize(); private: llvm::Module &module; + ObjcList retained; ObjcList protocols; ObjcList classes; void genImageInfo(); + void retainSymbols(); }; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index bfdc7c0a1c0..f80d2a4bca8 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -792,9 +792,12 @@ class ImplicitArgumentsBuilder { if (irFty.arg_objcSelector) { assert(dfnval); + + auto method = gIR->objc.getMethodRef(dfnval->func); + assert(method); - auto selptr = gIR->objc.getMethodRef(dfnval->func)->get(); - args.push_back(DtoLoad(selptr->getType(), selptr)); + auto methodptr = method->get(); + args.push_back(DtoLoad(methodptr->getType(), methodptr)); } } diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 064d59b9f4e..990fb6f49a3 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -55,6 +55,11 @@ IrClass::IrClass(ClassDeclaration *cd) : IrAggr(cd) { } void IrClass::addInterfaceVtbls(ClassDeclaration *cd) { + + // No interface vtables in Objective-C + if (cd->classKind == ClassKind::objc) + return; + if (cd->baseClass && !cd->isInterfaceDeclaration()) { addInterfaceVtbls(cd->baseClass); } diff --git a/ir/irtypeaggr.cpp b/ir/irtypeaggr.cpp index a8ab7387697..cb8030392ee 100644 --- a/ir/irtypeaggr.cpp +++ b/ir/irtypeaggr.cpp @@ -64,6 +64,11 @@ void AggrTypeBuilder::addAggregate( if (n == 0) return; + // Objective-C instance variables are laid out at runtime. + // as such, we should not generate the aggregate body. + if (ad->classKind == ClassKind::objc) + return; + // Unions may lead to overlapping fields, and we need to flatten them for LLVM // IR. We usually take the first field (in declaration order) of an // overlapping set, but a literal with an explicit initializer for a dominated @@ -196,9 +201,9 @@ void AggrTypeBuilder::addAggregate( if (vd->offset < m_offset) { error(vd->loc, - "%s `%s` overlaps previous field. This is an ICE, please file an " + "%s `%s` @ %u overlaps previous field @ %u. This is an ICE, please file an " "LDC issue.", - vd->kind(), vd->toPrettyChars()); + vd->kind(), vd->toPrettyChars(), vd->offset, m_offset); fatal(); } diff --git a/ir/irtypeclass.cpp b/ir/irtypeclass.cpp index 8e4a77d6fb8..85cd79d1d07 100644 --- a/ir/irtypeclass.cpp +++ b/ir/irtypeclass.cpp @@ -83,6 +83,14 @@ llvm::Type *IrTypeClass::getMemoryLLType() { AggrTypeBuilder builder; + // Objective-C just has an ISA pointer, so just + // throw that in there. + if (cd->classKind == ClassKind::objc) { + builder.addType(getOpaquePtrType(), target.ptrsize); + isaStruct(type)->setBody(builder.defaultTypes(), builder.isPacked()); + return type; + } + // add vtbl builder.addType(llvm::PointerType::get(vtbl_type, 0), target.ptrsize); From dd062ead7dc291582f3e1e9527037cc72a0d4ae8 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sat, 23 Nov 2024 07:02:00 +0100 Subject: [PATCH 22/67] Implicitly add isa pointer to objc classes. --- gen/objcgen.cpp | 8 ++++++-- ir/irtypeaggr.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 89f6b2e0aa9..dfca9e00f54 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -175,6 +175,10 @@ void ObjcObject::retain(LLGlobalVariable *toRetain) { objc.retained.push_back(toRetain); } +LLConstant *offsetIvar(size_t ivaroffset) { + return DtoConstUint(getPointerSize()+ivaroffset); +} + // // METHODS @@ -268,7 +272,7 @@ LLConstant *ObjcIvar::emit() { type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - offset->setInitializer(DtoConstUint(decl->offset)); + offset->setInitializer(offsetIvar(decl->offset)); } return nullptr; } @@ -391,7 +395,7 @@ const char *ObjcClasslike::getName() { LLGlobalVariable *ObjcClass::getIVarOffset(VarDeclaration *vd) { auto ivarsym = getObjcIvarSymbol(decl->ident->toChars(), vd->ident->toChars()); auto ivoffset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - ivoffset->setInitializer(DtoConstUint(vd->offset)); + ivoffset->setInitializer(offsetIvar(vd->offset)); this->retain(ivoffset); return ivoffset; diff --git a/ir/irtypeaggr.cpp b/ir/irtypeaggr.cpp index cb8030392ee..3bde723fdf7 100644 --- a/ir/irtypeaggr.cpp +++ b/ir/irtypeaggr.cpp @@ -66,8 +66,12 @@ void AggrTypeBuilder::addAggregate( // Objective-C instance variables are laid out at runtime. // as such, we should not generate the aggregate body. - if (ad->classKind == ClassKind::objc) - return; + if (auto klass = ad->isClassDeclaration()) { + if (klass->classKind == ClassKind::objc) { + this->addType(getOpaquePtrType(), getPointerSize()); + return; + } + } // Unions may lead to overlapping fields, and we need to flatten them for LLVM // IR. We usually take the first field (in declaration order) of an From 37a701db33a6161d181692b66d01a68e03718faf Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sat, 23 Nov 2024 13:33:02 +0100 Subject: [PATCH 23/67] Fix protocol referencing & allow pragma mangle --- gen/classes.cpp | 17 ++++++------- gen/objcgen.cpp | 67 ++++++++++++++++++++++++++----------------------- gen/objcgen.h | 5 +++- gen/runtime.cpp | 2 +- 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index 73cd006a270..bdca91098c6 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -364,7 +364,10 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { // Get class_t handle LLValue *objTy = getNullPtr(); if (auto chndl = _to->isClassHandle()) { - objTy = gIR->objc.getClassRef(chndl)->ref(); + if (auto ihndl = chndl->isInterfaceDeclaration()) + objTy = gIR->objc.getProtocolRef(ihndl)->ref(); + else + objTy = gIR->objc.getClassRef(chndl)->ref(); } // objc_opt_isKindOfClass will check if id is null @@ -424,19 +427,13 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // Get prototype_t handle LLValue *protoTy = getNullPtr(); if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) { - protoTy = gIR->objc.getProtocolRef(ifhndl)->get(); + protoTy = gIR->objc.getProtocolRef(ifhndl)->ref(); } // Class && kindOfProtocolFunc(Class) ? id : null LLValue *ret = gIR->ir->CreateSelect( - gIR->ir->CreateIsNotNull( - objClass - ), - gIR->ir->CreateSelect( - gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass, protoTy), - obj, - getNullPtr() - ), + gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass, protoTy), + obj, getNullPtr() ); return new DImValue(_to, ret); diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index dfca9e00f54..66f272b6aba 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -155,6 +155,10 @@ std::string getObjcProtoListSymbol(const char *name, bool isProtocol) { return getObjcSymbolName(isProtocol ? "_OBJC_PROTOCOL_PROTOCOLS_$_" : "_OBJC_CLASS_PROTOCOLS_$_", name); } +std::string getObjcProtoLabelSymbol(const char *name) { + return getObjcSymbolName("_OBJC_LABEL_PROTOCOL_$_", name); +} + std::string getObjcIvarSymbol(const char *className, const char *varName) { return ("OBJC_IVAR_$_" + std::string(className) + "." + std::string(varName)); } @@ -163,6 +167,14 @@ std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { return (std::string(dsymPrefix) + std::string(dsymName)); } +const char *getResolvedName(ClassDeclaration *decl) { + if (auto mo = decl->pMangleOverride) { + return mo->id->toChars(); + } + + return decl->objc.identifier->toChars(); +} + // // ObjcObject // @@ -333,8 +345,12 @@ bool ifaceListHas(ObjcList &list, InterfaceDeclaration * LLConstant *ObjcClasslike::emitProtocolList() { LLConstantList list; - auto ifaces = decl->interfaces; + + // Length + list.push_back(DtoConstUlong(ifaces.length)); + + // Protocols for(size_t i = 0; i < ifaces.length; i++) { if (auto iface = ifaces.ptr[i]) { @@ -342,12 +358,9 @@ LLConstant *ObjcClasslike::emitProtocolList() { // TODO: throw an error if you try to include a non-objective-c interface? if (auto ifacesym = (InterfaceDeclaration *)iface->sym) { if (ifacesym->classKind == ClassKind::objc) { - auto proto = getOrCreate( - getObjcProtoSymbol(ifacesym->ident->toChars()), - ObjcProtocol::getObjcProtocolType(module), "" - ); - - list.push_back(proto); + if (auto proto = this->objc.getProtocolRef(ifacesym)) { + list.push_back(proto->get()); + } } } } @@ -380,12 +393,12 @@ LLGlobalVariable *ObjcClasslike::emitName() { if (className) return className; - className = makeGlobalStr(decl->objc.identifier->toChars(), "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); + className = makeGlobalStr(getName(), "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); return className; } const char *ObjcClasslike::getName() { - return decl->objc.identifier->toChars(); + return getResolvedName(decl); } // @@ -538,7 +551,7 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { if (!meta) { // Base Protocols auto baseProtocols = emitProtocolList(); - protocolList = getOrCreate(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); + protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); // Instance variables @@ -578,7 +591,7 @@ LLConstant *ObjcClass::emit() { if (classTable) return classTable; - auto name = decl->objc.identifier->toChars(); + auto name = getName(); auto className = getObjcClassSymbol(name, false); auto metaName = getObjcClassSymbol(name, true); @@ -666,7 +679,7 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { // Base Protocols auto baseProtocols = this->emitProtocolList(); - auto protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); + auto protocolList = getOrCreate(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); // Class methods @@ -702,24 +715,14 @@ LLConstant *ObjcProtocol::emit() { if (protoref) return protoref; - auto name = decl->objc.identifier->toChars(); + auto name = getName(); auto protoName = getObjcProtoSymbol(name); - - // Extern classes only need non-ro refs. - if (decl->objc.isExtern) { - this->scan(); - - protocolTable = makeGlobal(protoName, nullptr, OBJC_SECNAME_DATA, true, false); - protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS, true, false); - return protoref; - } - + auto protoLabel = getObjcProtoLabelSymbol(name); // If we were weakly declared before, go grab our declarations. // Otherwise, create all the base tables for the type. protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); - protoref = makeGlobalRef(protocolTable, "objc_protoref", OBJC_SECNAME_PROTOREFS); - + protoref = makeGlobalRef(protocolTable, protoLabel, OBJC_SECNAME_PROTOREFS); // Emit their structure. this->scan(); @@ -728,7 +731,8 @@ LLConstant *ObjcProtocol::emit() { this->retain(protocolTable); this->retain(protoref); - return protoref; + + return protocolTable; } @@ -829,16 +833,17 @@ LLGlobalVariable *ObjCState::getIVarOffset(ClassDeclaration *cd, VarDeclaration } void ObjCState::emit(ClassDeclaration *cd) { - - // Meta-classes are emitted automatically with the class, - // as such we only need to emit a classref for the base class. - if (!cd || cd->objc.isMeta) - return; + // Protocols should more or less always be emitted. if (auto id = cd->isInterfaceDeclaration()) { getProtocolRef(id); return; } + + // Meta-classes are emitted automatically with the class, + // as such we only need to emit a classref for the base class. + if (!cd || cd->objc.isMeta) + return; getClassRef(cd); } diff --git a/gen/objcgen.h b/gen/objcgen.h index 92ffd9fff3e..b1d763b0ed6 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -55,7 +55,7 @@ class ObjCState; #define OBJC_SECNAME_CLASSLIST "__DATA,__objc_classlist, regular, no_dead_strip" #define OBJC_SECNAME_STUBS "__DATA,__objc_stubs, regular, no_dead_strip" #define OBJC_SECNAME_CATLIST "__DATA,__objc_catlist, regular, no_dead_strip" -#define OBJC_SECNAME_PROTOLIST "__DATA,__objc_protolist, regular, no_dead_strip" +#define OBJC_SECNAME_PROTOLIST "__DATA,__objc_protolist, coalesced, no_dead_strip" #define OBJC_SECNAME_PROTOREFS "__DATA,__objc_protorefs, regular" #define OBJC_SECNAME_CONST "__DATA,__objc_const" #define OBJC_SECNAME_DATA "__DATA,__objc_data" @@ -346,6 +346,9 @@ class ObjcProtocol : public ObjcClasslike { return protocolTable; } + // Gets the protocol ref. + LLConstant *ref() { return protoref; } + protected: // Called to emit the object. diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 9e00ee4c1b2..06b99777411 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -869,7 +869,7 @@ static void buildRuntimeModule() { {objectPtrTy, objectPtrTy}, {}, AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); - // class_conformsToProtocol(Class cls, Protocol *protocol) + // bool class_conformsToProtocol(Class cls, Protocol *protocol) createFwdDecl(LINK::c, boolTy, {"class_conformsToProtocol"}, {objectPtrTy, objectPtrTy}, {}, AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); From f0a5b4cc26f77055e641d195fb11ce524e2a6a33 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sat, 23 Nov 2024 14:46:23 +0100 Subject: [PATCH 24/67] Fix protocol linkage --- gen/objcgen.cpp | 23 +++++++++++------------ gen/objcgen.h | 12 ++++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 66f272b6aba..091ba9bbc38 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -679,17 +679,17 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { // Base Protocols auto baseProtocols = this->emitProtocolList(); - auto protocolList = getOrCreate(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); + auto protocolList = getOrCreateWeak(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); // Class methods auto classMethodConsts = this->emitMethodList(classMethods); - auto classMethodList = getOrCreate(getObjcProtoMethodListSymbol(getName(), true), classMethodConsts->getType(), OBJC_SECNAME_CONST); + auto classMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true), classMethodConsts->getType(), OBJC_SECNAME_CONST); classMethodList->setInitializer(classMethodConsts); // Instance methods auto instanceMethodConsts = this->emitMethodList(instanceMethods); - auto instanceMethodList = getOrCreate(getObjcProtoMethodListSymbol(getName(), false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); + auto instanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); instanceMethodList->setInitializer(instanceMethodConsts); members.push_back(getNullPtr()); // isa @@ -710,19 +710,19 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { } LLConstant *ObjcProtocol::emit() { - - // Protocol already exists, just return that. - if (protoref) - return protoref; + if (protocolTable) + return protocolTable; auto name = getName(); auto protoName = getObjcProtoSymbol(name); auto protoLabel = getObjcProtoLabelSymbol(name); - // If we were weakly declared before, go grab our declarations. - // Otherwise, create all the base tables for the type. - protocolTable = getOrCreate(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); - protoref = makeGlobalRef(protocolTable, protoLabel, OBJC_SECNAME_PROTOREFS); + // We want it to be locally hidden and weak since the protocols + // may be declared in multiple object files. + protocolTable = getOrCreateWeak(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); + protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOREFS); + protoref->setInitializer(protocolTable); + // Emit their structure. this->scan(); @@ -731,7 +731,6 @@ LLConstant *ObjcProtocol::emit() { this->retain(protocolTable); this->retain(protoref); - return protocolTable; } diff --git a/gen/objcgen.h b/gen/objcgen.h index b1d763b0ed6..c33dba5d01a 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -118,6 +118,18 @@ class ObjcObject { return makeGlobal(name, type, section, true, extInitializer); } + // Gets a global variable or creates it. + LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false) { + auto global = module.getGlobalVariable(name, true); + if (global) + return global; + + global = makeGlobal(name, type, section, false, extInitializer); + global->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + global->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); + return global; + } + // The module the object resides in. llvm::Module &module; From 1b2cc5d138443592ab062fe5031b155ca4adb9d6 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sat, 23 Nov 2024 15:24:16 +0100 Subject: [PATCH 25/67] Fix direct call support --- gen/abi/aarch64.cpp | 4 ++-- gen/abi/abi.cpp | 2 +- gen/abi/abi.h | 2 +- gen/abi/arm.cpp | 6 +++--- gen/abi/x86-64.cpp | 8 ++++---- gen/abi/x86.cpp | 6 +++--- gen/objcgen.cpp | 15 --------------- gen/objcgen.h | 3 --- gen/tocall.cpp | 4 ++-- 9 files changed, 16 insertions(+), 34 deletions(-) diff --git a/gen/abi/aarch64.cpp b/gen/abi/aarch64.cpp index ad3e6f1d7c9..2a9f58639e5 100644 --- a/gen/abi/aarch64.cpp +++ b/gen/abi/aarch64.cpp @@ -175,11 +175,11 @@ struct AArch64TargetABI : TargetABI { return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); } - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override { assert(isDarwin()); // see objc/message.h for objc_msgSend selection rules - return superCall ? "objc_msgSendSuper" : "objc_msgSend"; + return directcall ? "objc_msgSendSuper" : "objc_msgSend"; } }; diff --git a/gen/abi/abi.cpp b/gen/abi/abi.cpp index 99cc18457c6..b034f65072f 100644 --- a/gen/abi/abi.cpp +++ b/gen/abi/abi.cpp @@ -207,7 +207,7 @@ Type *TargetABI::vaListType() { ////////////////////////////////////////////////////////////////////////////// -const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) { +const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) { llvm_unreachable("Unknown Objective-C ABI"); } diff --git a/gen/abi/abi.h b/gen/abi/abi.h index 786a717b782..02656ab4231 100644 --- a/gen/abi/abi.h +++ b/gen/abi/abi.h @@ -177,7 +177,7 @@ struct TargetABI { virtual Type *vaListType(); /// Returns Objective-C message send function - virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall); + virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall); /***** Static Helpers *****/ diff --git a/gen/abi/arm.cpp b/gen/abi/arm.cpp index 34101c8b61a..5c1107e1c5a 100644 --- a/gen/abi/arm.cpp +++ b/gen/abi/arm.cpp @@ -123,14 +123,14 @@ struct ArmTargetABI : TargetABI { return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); } - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override { assert(isDarwin()); // see objc/message.h for objc_msgSend selection rules if (fty.arg_sret) { - return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; + return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } - return superCall ? "objc_msgSendSuper" : "objc_msgSend"; + return directcall ? "objc_msgSendSuper" : "objc_msgSend"; } }; diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index 9c47823dac4..737b265c51a 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -165,7 +165,7 @@ struct X86_64TargetABI : TargetABI { Type *vaListType() override; - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override; + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override; private: LLType *getValistType(); @@ -379,18 +379,18 @@ Type *X86_64TargetABI::vaListType() { TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag"))); } -const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) { +const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) { assert(isDarwin()); // see objc/message.h for objc_msgSend selection rules if (fty.arg_sret) { - return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; + return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } // float, double, long double return if (ret && ret->isfloating() && !ret->iscomplex()) { return "objc_msgSend_fpret"; } - return superCall ? "objc_msgSendSuper" : "objc_msgSend"; + return directcall ? "objc_msgSendSuper" : "objc_msgSend"; } // The public getter for abi.cpp diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index 99169cb259c..665ce7d49d3 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -271,18 +271,18 @@ struct X86TargetABI : TargetABI { } } - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool superCall) override { + const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override { assert(isDarwin()); // see objc/message.h for objc_msgSend selection rules if (fty.arg_sret) { - return superCall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; + return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } // float, double, long double return if (ret && ret->isfloating() && !ret->iscomplex()) { return "objc_msgSend_fpret"; } - return superCall ? "objc_msgSendSuper" : "objc_msgSend"; + return directcall ? "objc_msgSendSuper" : "objc_msgSend"; } }; diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 091ba9bbc38..272ee373e3f 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -645,21 +645,6 @@ LLValue *ObjcClass::ref() { return deref(classTable); } -LLValue *ObjcClass::getRefFor(LLValue *id) { - if (decl->objc.isExtern) { - - // We can't be sure that the isa "pointer" is actually a pointer to a class - // In extern scenarios, therefore we call object_getClass. - auto getClassFunc = getRuntimeFunction(decl->loc, module, "object_getClass"); - auto classref = gIR->CreateCallOrInvoke(getClassFunc, id, ""); - return deref(classref); - } - - // If we defined the type we can be 100% sure of the layout. - // so this is a fast path. - return deref(classTable); -} - LLConstant *ObjcClass::get() { isUsed = true; diff --git a/gen/objcgen.h b/gen/objcgen.h index c33dba5d01a..d46ed234b99 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -474,9 +474,6 @@ class ObjcClass : public ObjcClasslike { // Gets the root metaclass of this class. LLConstant *getRootMetaClass(); - // Gets the class reference for the specified id. - LLValue *getRefFor(LLValue *id); - protected: // Called to emit the object. diff --git a/gen/tocall.cpp b/gen/tocall.cpp index f80d2a4bca8..f1d30f0b309 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -736,14 +736,14 @@ class ImplicitArgumentsBuilder { if (objccall && directcall) { - // ... or a Objective-c super call argument + // ... or a Objective-c direct call argument if (auto parentfd = dfnval->func->isFuncDeclaration()) { if (auto cls = parentfd->parent->isClassDeclaration()) { // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( dfnval->vthis, - DtoLoad(getOpaquePtrType(), gIR->objc.getClassRef(cls)->getRefFor(dfnval->vthis)), + gIR->objc.getClassRef(cls)->ref(), "super" ); From 8212b129d5ef734c502dd49212c649ffe47f5146 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sun, 24 Nov 2024 02:53:35 +0100 Subject: [PATCH 26/67] always generate var type for methods --- gen/objcgen.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 272ee373e3f..e5e9694793b 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -197,15 +197,6 @@ LLConstant *offsetIvar(size_t ivaroffset) { // LLConstant *ObjcMethod::emit() { - - // Extern declarations don't need to define - // a var type. - if (!decl->fbody) { - name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); - selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, true, true); - return selref; - } - name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, true, true); From aa9b892aa2709c8a025b7efbca48cdd3720f9ca4 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sun, 24 Nov 2024 03:21:36 +0100 Subject: [PATCH 27/67] Fix test 16096a --- gen/objcgen.cpp | 7 ++++++- tests/dmd/runnable/extra-files/test16096a.d | 13 +++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index e5e9694793b..f4a3d3ad7e5 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -620,6 +620,11 @@ LLConstant *ObjcClass::emit() { this->emitTable(metaClassTable, getRootMetaClass(), getSuper(true), metaClassRoTable); this->emitRoTable(classRoTable, false); this->emitRoTable(metaClassRoTable, true); + + this->retain(classTable); + this->retain(metaClassTable); + this->retain(classRoTable); + this->retain(metaClassRoTable); return classTable; } @@ -828,7 +833,7 @@ void ObjCState::emit(ClassDeclaration *cd) { // void ObjCState::finalize() { - size_t totalObjects = classes.size()+protocols.size()+retained.size(); + size_t totalObjects = retained.size(); if (totalObjects > 0) { genImageInfo(); diff --git a/tests/dmd/runnable/extra-files/test16096a.d b/tests/dmd/runnable/extra-files/test16096a.d index b6890ec5030..4be18c738e4 100644 --- a/tests/dmd/runnable/extra-files/test16096a.d +++ b/tests/dmd/runnable/extra-files/test16096a.d @@ -3,22 +3,19 @@ module test16096a; import core.attribute : selector; extern (Objective-C) -interface Class -{ +interface Class { NSObject alloc() @selector("alloc"); } extern (Objective-C) -interface NSObject -{ - NSObject initWithUTF8String(in char* str) @selector("initWithUTF8String:"); +interface NSObject { + NSObject initWithUTF8String(const(char)* str) @selector("initWithUTF8String:"); void release() @selector("release"); } -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(const(char)* name); -void test() -{ +void test() { auto c = objc_lookUpClass("NSString"); auto o = c.alloc().initWithUTF8String("hello"); o.release(); From 29c9616ff54ddfa575adf1c38c6bb0982f1f1f8c Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Sun, 24 Nov 2024 17:44:27 +0100 Subject: [PATCH 28/67] Fix extern ivar symbol gen, retain method decls --- gen/objcgen.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index f4a3d3ad7e5..f7b4cec14db 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -199,7 +199,11 @@ LLConstant *offsetIvar(size_t ivaroffset) { LLConstant *ObjcMethod::emit() { name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); - selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, true, true); + selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); + + this->retain(name); + this->retain(type); + this->retain(selref); return selref; } @@ -264,9 +268,12 @@ LLConstant *ObjcIvar::emit() { // Extern, emit data. if (auto klass = decl->parent->isClassDeclaration()) { if (klass->objc.isExtern) { - name = makeGlobal(ivarsym, nullptr, "OBJC_METH_VAR_NAME_", true, true); - type = makeGlobal(ivarsym, nullptr, "OBJC_METH_VAR_TYPE_", true, true); + name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true); + type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true); offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + + // It will be filled out by the runtime, but make sure it's there nontheless. + offset->setInitializer(offsetIvar(0)); return nullptr; } From a885a90384c711cb92f3b745ca3f261be060c0d0 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 03:41:33 +0100 Subject: [PATCH 29/67] Remove arm32 and x86 support --- driver/linker-gcc.cpp | 2 +- driver/linker.cpp | 8 -------- driver/linker.h | 5 ----- gen/abi/arm.cpp | 10 ---------- gen/abi/x86-64.cpp | 4 ++-- gen/abi/x86.cpp | 14 -------------- gen/objcgen.cpp | 6 +----- gen/runtime.cpp | 7 ++----- 8 files changed, 6 insertions(+), 50 deletions(-) diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index 251c7e58e0a..d3c33cca173 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -476,7 +476,7 @@ void ArgsBuilder::addObjcStdlibLinkFlags(const llvm::Triple &triple) { if (linkNoObjc) return; - args.push_back(("-l"+getObjcLibName()).str()); + args.push_back("-lobjc"); } // Adds all required link flags for PGO. diff --git a/driver/linker.cpp b/driver/linker.cpp index b593994b063..bd309e83b71 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -257,14 +257,6 @@ llvm::StringRef getMscrtLibName(const bool *useInternalToolchain) { ////////////////////////////////////////////////////////////////////////////// -llvm::StringRef getObjcLibName() { - // TODO: Support scanning for and loading alternate - // objective-c runtimes like the GNUStep runtime? - return "objc"; -} - -////////////////////////////////////////////////////////////////////////////// - /// Insert an LLVM bitcode file into the module static void insertBitcodeIntoModule(const char *bcFile, llvm::Module &M, llvm::LLVMContext &Context) { diff --git a/driver/linker.h b/driver/linker.h index 78478af0f2e..28362d86526 100644 --- a/driver/linker.h +++ b/driver/linker.h @@ -58,11 +58,6 @@ llvm::StringRef getExplicitMscrtLibName(); */ llvm::StringRef getMscrtLibName(const bool *useInternalToolchain = nullptr); -/** - * Returns the name of the Objective-C runtime library to link with. - */ -llvm::StringRef getObjcLibName(); - /** * Inserts bitcode files passed on the commandline into a module. */ diff --git a/gen/abi/arm.cpp b/gen/abi/arm.cpp index 5c1107e1c5a..ed4c311a223 100644 --- a/gen/abi/arm.cpp +++ b/gen/abi/arm.cpp @@ -122,16 +122,6 @@ struct ArmTargetABI : TargetABI { // solution is found there, this should be adapted). return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); } - - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override { - assert(isDarwin()); - - // see objc/message.h for objc_msgSend selection rules - if (fty.arg_sret) { - return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; - } - return directcall ? "objc_msgSendSuper" : "objc_msgSend"; - } }; TargetABI *getArmTargetABI() { return new ArmTargetABI; } diff --git a/gen/abi/x86-64.cpp b/gen/abi/x86-64.cpp index 737b265c51a..f73d81f043a 100644 --- a/gen/abi/x86-64.cpp +++ b/gen/abi/x86-64.cpp @@ -387,8 +387,8 @@ const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool dire return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; } // float, double, long double return - if (ret && ret->isfloating() && !ret->iscomplex()) { - return "objc_msgSend_fpret"; + if (ret && ret->isfloating()) { + return ret->ty == TY::Tcomplex80 ? "objc_msgSend_fp2ret" : "objc_msgSend_fpret"; } return directcall ? "objc_msgSendSuper" : "objc_msgSend"; } diff --git a/gen/abi/x86.cpp b/gen/abi/x86.cpp index 665ce7d49d3..5e8cd9eb949 100644 --- a/gen/abi/x86.cpp +++ b/gen/abi/x86.cpp @@ -270,20 +270,6 @@ struct X86TargetABI : TargetABI { } } } - - const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override { - assert(isDarwin()); - - // see objc/message.h for objc_msgSend selection rules - if (fty.arg_sret) { - return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret"; - } - // float, double, long double return - if (ret && ret->isfloating() && !ret->iscomplex()) { - return "objc_msgSend_fpret"; - } - return directcall ? "objc_msgSendSuper" : "objc_msgSend"; - } }; // The public getter for abi.cpp. diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index f7b4cec14db..46b72303b75 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -25,14 +25,10 @@ bool objc_isSupported(const llvm::Triple &triple) { // Additionally only Objective-C 2 is supported. switch (triple.getArch()) { case llvm::Triple::aarch64: // arm64 iOS, tvOS, macOS, watchOS, visionOS - case llvm::Triple::arm: // armv6 iOS - case llvm::Triple::thumb: // thumbv7 iOS, watchOS case llvm::Triple::x86_64: // OSX, iOS, tvOS sim return true; - case llvm::Triple::x86: // OSX, iOS, watchOS sim - return false; default: - break; + return false; } } return false; diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 06b99777411..3df65289fc0 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -880,15 +880,12 @@ static void buildRuntimeModule() { // creal objc_msgSend_fp2ret(id self, SEL op, ...) createFwdDecl(LINK::c, Type::tcomplex80, {"objc_msgSend_fp2ret"}, {objectPtrTy, selectorPtrTy}); - // fall-thru - case llvm::Triple::x86: + // x86_64 real return only, x86 float, double, real return // real objc_msgSend_fpret(id self, SEL op, ...) createFwdDecl(LINK::c, realTy, {"objc_msgSend_fpret"}, {objectPtrTy, selectorPtrTy}); - // fall-thru - case llvm::Triple::arm: - case llvm::Triple::thumb: + // used when return value is aggregate via a hidden sret arg // void objc_msgSend_stret(T *sret_arg, id self, SEL op, ...) createFwdDecl(LINK::c, voidTy, {"objc_msgSend_stret"}, From 7d857c953d1e1fa7ce50b5c3700b339ca69a4551 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 05:00:57 +0100 Subject: [PATCH 30/67] Check method and ivar info before pushing to member list --- gen/objcgen.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 46b72303b75..5e2f7377dd7 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -372,8 +372,10 @@ LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods) { // Find out how many functions have actual bodies. for(size_t i = 0; i < methods.size(); i++) { - if (methods[i]->decl->fbody) - toAdd.push_back(methods[i]->info()); + auto methodInfo = methods[i]->info(); + + if (methodInfo) + toAdd.push_back(methodInfo); } return ObjcObject::emitList( @@ -448,7 +450,10 @@ LLConstant *ObjcClass::emitIvarList() { // Push on all the ivars. for(size_t i = 0; i < ivars.size(); i++) { - members.push_back(ivars[i]->info()); + auto ivarInfo = ivars[i]->info(); + + if (ivarInfo) + members.push_back(ivarInfo); } return LLConstantStruct::getAnon( @@ -565,7 +570,7 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { members.push_back(wrapNull(protocolList)); members.push_back(wrapNull(ivarList)); members.push_back(getNullPtr()); - members.push_back(getNullPtr()); //TODO: Add properties? + members.push_back(getNullPtr()); table->setInitializer(LLConstantStruct::get( getObjcClassRoType(module), From def16ff402483942477a065aaa1b6ef95dd49edf Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 05:09:12 +0100 Subject: [PATCH 31/67] Make ObjcMethod info untyped. --- gen/objcgen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 5e2f7377dd7..8e58ab3931b 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -211,8 +211,7 @@ LLConstant *ObjcMethod::info() { if (!decl->fbody) return nullptr; - return LLConstantStruct::get( - getObjcMethodType(module), + return LLConstantStruct::getAnon( { name, type, DtoCallee(decl) } ); } From 3b8687387f8ed68efd91e72db0b4edf1a0329f18 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 06:52:27 +0100 Subject: [PATCH 32/67] Make ivar and method gen more robust --- gen/objcgen.cpp | 108 ++++++++++++++++++++++++++++++++---------------- gen/objcgen.h | 29 ++++++++----- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 8e58ab3931b..81e63ea0732 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -211,8 +211,11 @@ LLConstant *ObjcMethod::info() { if (!decl->fbody) return nullptr; - return LLConstantStruct::getAnon( - { name, type, DtoCallee(decl) } + auto func = DtoCallee(decl); + + return LLConstantStruct::get( + ObjcMethod::getObjcMethodType(module, func), + { name, type, func } ); } @@ -224,11 +227,14 @@ LLConstant *ObjcMethod::get() { return selref; } -LLConstant *ObjcObject::emitList(llvm::Module &module, LLType *elemType, LLConstantList objects, bool isCountPtrSized) { +LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, size_t allocSize, bool isCountPtrSized) { LLConstantList members; + // Emit nullptr for empty lists. + if (objects.empty()) + return nullptr; + // Size of stored struct. - size_t allocSize = getTypeAllocSize(elemType); members.push_back( isCountPtrSized ? DtoConstSize_t(allocSize) : @@ -360,27 +366,35 @@ LLConstant *ObjcClasslike::emitProtocolList() { } return ObjcObject::emitList( - module, - getOpaquePtrType(), - list + module, + list, + getPointerSize() ); } -LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods) { +LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods, bool optionalMethods) { LLConstantList toAdd; - + + // Emit nullptr for empty lists. + if (methods.empty()) + return nullptr; + // Find out how many functions have actual bodies. for(size_t i = 0; i < methods.size(); i++) { - auto methodInfo = methods[i]->info(); + auto method = methods[i]; + + if (method->isOptional() == optionalMethods) { + auto methodInfo = method->info(); - if (methodInfo) - toAdd.push_back(methodInfo); + if (methodInfo) + toAdd.push_back(methodInfo); + } } return ObjcObject::emitList( module, - ObjcMethod::getObjcMethodType(module), - toAdd + toAdd, + ObjcMethod::getObjcMethodTypeSize() ); } @@ -543,8 +557,10 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { this->emitMethodList(classMethods) : this->emitMethodList(instanceMethods); - methodList = getOrCreate(getObjcClassMethodListSymbol(getName(), meta), baseMethods->getType(), OBJC_SECNAME_CONST); - methodList->setInitializer(baseMethods); + if (baseMethods) { + methodList = getOrCreate(getObjcClassMethodListSymbol(getName(), meta), baseMethods->getType(), OBJC_SECNAME_CONST); + methodList->setInitializer(baseMethods); + } if (!meta) { // Base Protocols @@ -664,32 +680,52 @@ LLConstant *ObjcClass::get() { void ObjcProtocol::emitTable(LLGlobalVariable *table) { size_t allocSize = getTypeAllocSize(getObjcProtocolType(module)); LLConstantList members; + LLGlobalVariable *protocolList = nullptr; + LLGlobalVariable *classMethodList = nullptr; + LLGlobalVariable *instanceMethodList = nullptr; + LLGlobalVariable *optClassMethodList = nullptr; + LLGlobalVariable *optInstanceMethodList = nullptr; // Base Protocols - auto baseProtocols = this->emitProtocolList(); - auto protocolList = getOrCreateWeak(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); - protocolList->setInitializer(baseProtocols); + if (auto baseProtocols = this->emitProtocolList()) { + protocolList = getOrCreateWeak(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); + protocolList->setInitializer(baseProtocols); + } // Class methods - auto classMethodConsts = this->emitMethodList(classMethods); - auto classMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true), classMethodConsts->getType(), OBJC_SECNAME_CONST); - classMethodList->setInitializer(classMethodConsts); + if (auto classMethodConsts = this->emitMethodList(classMethods)) { + classMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true), classMethodConsts->getType(), OBJC_SECNAME_CONST); + classMethodList->setInitializer(classMethodConsts); + } + + // Optional class methods + if (auto optClassMethodConsts = this->emitMethodList(classMethods, true)) { + optClassMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true), optClassMethodConsts->getType(), OBJC_SECNAME_CONST); + optClassMethodList->setInitializer(optClassMethodConsts); + } // Instance methods - auto instanceMethodConsts = this->emitMethodList(instanceMethods); - auto instanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); - instanceMethodList->setInitializer(instanceMethodConsts); - - members.push_back(getNullPtr()); // isa - members.push_back(emitName()); // mangledName - members.push_back(protocolList); // protocol_list - members.push_back(instanceMethodList); // instanceMethods - members.push_back(classMethodList); // classMethods - members.push_back(getNullPtr()); // optionalInstanceMethods (TODO) - members.push_back(getNullPtr()); // optionalClassMethods (TODO) - members.push_back(getNullPtr()); // instanceProperties (TODO) - members.push_back(DtoConstUint(allocSize)); // size - members.push_back(DtoConstUint(0)); // flags + if (auto instanceMethodConsts = this->emitMethodList(instanceMethods)) { + instanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); + instanceMethodList->setInitializer(instanceMethodConsts); + } + + // Optional instance methods + if (auto optInstanceMethodConsts = this->emitMethodList(instanceMethods, true)) { + optInstanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false), optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); + optInstanceMethodList->setInitializer(optInstanceMethodConsts); + } + + members.push_back(getNullPtr()); // isa + members.push_back(emitName()); // mangledName + members.push_back(wrapNull(protocolList)); // protocol_list + members.push_back(wrapNull(instanceMethodList)); // instanceMethods + members.push_back(wrapNull(classMethodList)); // classMethods + members.push_back(wrapNull(optInstanceMethodList)); // optionalInstanceMethods (TODO) + members.push_back(wrapNull(optClassMethodList)); // optionalClassMethods (TODO) + members.push_back(getNullPtr()); // instanceProperties (TODO) + members.push_back(DtoConstUint(allocSize)); // size + members.push_back(DtoConstUint(0)); // flags table->setInitializer(LLConstantStruct::get( getObjcProtocolType(module), diff --git a/gen/objcgen.h b/gen/objcgen.h index d46ed234b99..df7a3ae227f 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -105,7 +105,7 @@ class ObjcObject { virtual const char *getName() { return nullptr; } // Emits a new list for the specified objects as a constant. - static LLConstant *emitList(llvm::Module &module, LLType *elemType, LLConstantList objects, bool isCountPtrSized = false); + static LLConstant *emitList(llvm::Module &module, LLConstantList objects, size_t allocSize, bool isCountPtrSized = false); protected: @@ -154,22 +154,26 @@ class ObjcMethod : public ObjcObject { // Gets the main reference to the object. LLConstant *get() override; - // Gets the type of an Objective-C class_t struct - static LLStructType *getObjcMethodType(const llvm::Module& module) { - auto methodType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_METHOD); - if (methodType) - return methodType; + // Since each objcMethodType differs due to llvm::Function apparently + // not being compatible with opaque pointers, we emit a type size here. + // + // This should be updated if apple changes the Objective-C ABI + // again. + static size_t getObjcMethodTypeSize() { + return getPointerSize()*3; + } - methodType = LLStructType::create( + // Gets the type of an Objective-C objc_method struct + static LLStructType *getObjcMethodType(const llvm::Module& module, LLFunction* func) { + return LLStructType::create( module.getContext(), { getOpaquePtrType(), // SEL name getOpaquePtrType(), // const char *types - getOpaquePtrType(), // IMP imp + func->getType(), // IMP imp }, OBJC_STRUCTNAME_METHOD ); - return methodType; } // Emits the constant struct containing the method @@ -182,6 +186,11 @@ class ObjcMethod : public ObjcObject { return LLStringRef(selector->stringvalue, selector->stringlen); } + // Gets whether the function is optional. + bool isOptional() { + return decl->objc.isOptional; + } + protected: // Called to emit the object. @@ -297,7 +306,7 @@ class ObjcClasslike : public ObjcObject { virtual void onScan(bool meta); // Emits a method list as a constant. - LLConstant *emitMethodList(std::vector &methods); + LLConstant *emitMethodList(std::vector &methods, bool optionalMethods=false); // Emits a method list as a constant. LLConstant *emitProtocolList(); From 056e45716abfa04b4cbb069b8566e1efc7cf7cb8 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 08:42:22 +0100 Subject: [PATCH 33/67] Generate optional protocol symbols --- gen/objcgen.cpp | 95 +++++++++++++++++++++++++------------------------ gen/objcgen.h | 15 ++------ 2 files changed, 51 insertions(+), 59 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 81e63ea0732..dc3f52d8780 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -135,8 +135,10 @@ std::string getObjcClassMethodListSymbol(const char *className, bool meta) { return getObjcSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className); } -std::string getObjcProtoMethodListSymbol(const char *className, bool meta) { - return getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className); +std::string getObjcProtoMethodListSymbol(const char *className, bool meta, bool optional) { + return optional ? + getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_OPTIONAL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_OPTIONAL_INSTANCE_METHODS_", className) : + getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className); } std::string getObjcIvarListSymbol(const char *className) { @@ -187,6 +189,39 @@ LLConstant *offsetIvar(size_t ivaroffset) { return DtoConstUint(getPointerSize()+ivaroffset); } +LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, bool isCountPtrSized) { + LLConstantList members; + + // Emit nullptr for empty lists. + if (objects.empty()) + return nullptr; + + // Size of stored struct. + size_t allocSize = getTypeAllocSize(objects.front()->getType()); + members.push_back( + isCountPtrSized ? + DtoConstSize_t(allocSize) : + DtoConstUint(allocSize) + ); + + // Method count + members.push_back(DtoConstUint( + objects.size() + )); + + // Insert all the objects in the constant list. + members.insert( + members.end(), + objects.begin(), + objects.end() + ); + + return LLConstantStruct::getAnon( + members, + true + ); +} + // // METHODS @@ -215,7 +250,7 @@ LLConstant *ObjcMethod::info() { return LLConstantStruct::get( ObjcMethod::getObjcMethodType(module, func), - { name, type, func } + { name, type, DtoBitCast(func, getOpaquePtrType()) } ); } @@ -227,38 +262,6 @@ LLConstant *ObjcMethod::get() { return selref; } -LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, size_t allocSize, bool isCountPtrSized) { - LLConstantList members; - - // Emit nullptr for empty lists. - if (objects.empty()) - return nullptr; - - // Size of stored struct. - members.push_back( - isCountPtrSized ? - DtoConstSize_t(allocSize) : - DtoConstUint(allocSize) - ); - - // Method count - members.push_back(DtoConstUint( - objects.size() - )); - - // Insert all the objects in the constant list. - members.insert( - members.end(), - objects.begin(), - objects.end() - ); - - return LLConstantStruct::getAnon( - members, - true - ); -} - // // INSTANCE VARIABLES // @@ -271,9 +274,9 @@ LLConstant *ObjcIvar::emit() { if (klass->objc.isExtern) { name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true); type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true); - offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); // It will be filled out by the runtime, but make sure it's there nontheless. + offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); offset->setInitializer(offsetIvar(0)); return nullptr; } @@ -367,8 +370,7 @@ LLConstant *ObjcClasslike::emitProtocolList() { return ObjcObject::emitList( module, - list, - getPointerSize() + list ); } @@ -393,8 +395,7 @@ LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods, bool return ObjcObject::emitList( module, - toAdd, - ObjcMethod::getObjcMethodTypeSize() + toAdd ); } @@ -694,35 +695,35 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { // Class methods if (auto classMethodConsts = this->emitMethodList(classMethods)) { - classMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true), classMethodConsts->getType(), OBJC_SECNAME_CONST); + classMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true, false), classMethodConsts->getType(), OBJC_SECNAME_CONST); classMethodList->setInitializer(classMethodConsts); } // Optional class methods if (auto optClassMethodConsts = this->emitMethodList(classMethods, true)) { - optClassMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true), optClassMethodConsts->getType(), OBJC_SECNAME_CONST); + optClassMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true, true), optClassMethodConsts->getType(), OBJC_SECNAME_CONST); optClassMethodList->setInitializer(optClassMethodConsts); } // Instance methods if (auto instanceMethodConsts = this->emitMethodList(instanceMethods)) { - instanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); + instanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false, false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); instanceMethodList->setInitializer(instanceMethodConsts); } // Optional instance methods if (auto optInstanceMethodConsts = this->emitMethodList(instanceMethods, true)) { - optInstanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false), optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); + optInstanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false, true), optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); optInstanceMethodList->setInitializer(optInstanceMethodConsts); } members.push_back(getNullPtr()); // isa + members.push_back(wrapNull(protocolList)); // protocols members.push_back(emitName()); // mangledName - members.push_back(wrapNull(protocolList)); // protocol_list members.push_back(wrapNull(instanceMethodList)); // instanceMethods members.push_back(wrapNull(classMethodList)); // classMethods - members.push_back(wrapNull(optInstanceMethodList)); // optionalInstanceMethods (TODO) - members.push_back(wrapNull(optClassMethodList)); // optionalClassMethods (TODO) + members.push_back(wrapNull(optInstanceMethodList)); // optionalInstanceMethods + members.push_back(wrapNull(optClassMethodList)); // optionalClassMethods members.push_back(getNullPtr()); // instanceProperties (TODO) members.push_back(DtoConstUint(allocSize)); // size members.push_back(DtoConstUint(0)); // flags diff --git a/gen/objcgen.h b/gen/objcgen.h index df7a3ae227f..d4aa8b5c4da 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -83,7 +83,7 @@ std::string getObjcClassSymbol(const char *name, bool meta); std::string getObjcClassMethodListSymbol(const char *className, bool meta); std::string getObjcIvarListSymbol(const char *className); std::string getObjcIvarSymbol(const char *className, const char *varName); -std::string getObjcProtoMethodListSymbol(const char *className, bool meta); +std::string getObjcProtoMethodListSymbol(const char *className, bool meta, bool optional); std::string getObjcProtoSymbol(const char *name); std::string getObjcProtoListSymbol(const char *name); std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); @@ -105,7 +105,7 @@ class ObjcObject { virtual const char *getName() { return nullptr; } // Emits a new list for the specified objects as a constant. - static LLConstant *emitList(llvm::Module &module, LLConstantList objects, size_t allocSize, bool isCountPtrSized = false); + static LLConstant *emitList(llvm::Module &module, LLConstantList objects, bool isCountPtrSized = false); protected: @@ -154,15 +154,6 @@ class ObjcMethod : public ObjcObject { // Gets the main reference to the object. LLConstant *get() override; - // Since each objcMethodType differs due to llvm::Function apparently - // not being compatible with opaque pointers, we emit a type size here. - // - // This should be updated if apple changes the Objective-C ABI - // again. - static size_t getObjcMethodTypeSize() { - return getPointerSize()*3; - } - // Gets the type of an Objective-C objc_method struct static LLStructType *getObjcMethodType(const llvm::Module& module, LLFunction* func) { return LLStructType::create( @@ -170,7 +161,7 @@ class ObjcMethod : public ObjcObject { { getOpaquePtrType(), // SEL name getOpaquePtrType(), // const char *types - func->getType(), // IMP imp + getOpaquePtrType(), // IMP imp }, OBJC_STRUCTNAME_METHOD ); From c1a76f805808abfa405de15dc4bc98cf9b1d65b5 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 10:25:25 +0100 Subject: [PATCH 34/67] Use bitcasting instead of creating multiple type defs --- gen/objcgen.cpp | 3 +-- gen/objcgen.h | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index dc3f52d8780..3c0795fbf78 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -247,9 +247,8 @@ LLConstant *ObjcMethod::info() { return nullptr; auto func = DtoCallee(decl); - return LLConstantStruct::get( - ObjcMethod::getObjcMethodType(module, func), + ObjcMethod::getObjcMethodType(module), { name, type, DtoBitCast(func, getOpaquePtrType()) } ); } diff --git a/gen/objcgen.h b/gen/objcgen.h index d4aa8b5c4da..40e209f4bdf 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -155,7 +155,11 @@ class ObjcMethod : public ObjcObject { LLConstant *get() override; // Gets the type of an Objective-C objc_method struct - static LLStructType *getObjcMethodType(const llvm::Module& module, LLFunction* func) { + static LLStructType *getObjcMethodType(const llvm::Module& module) { + auto methType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_METHOD); + if (methType) + return methType; + return LLStructType::create( module.getContext(), { From 442f248dfb47f567db55df07feb02caf0ebcc018 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 10:53:50 +0100 Subject: [PATCH 35/67] Fix invalid protocol list struct gen --- gen/objcgen.cpp | 19 +++++++++---------- gen/tollvm.cpp | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 3c0795fbf78..22454302038 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -348,9 +348,6 @@ LLConstant *ObjcClasslike::emitProtocolList() { LLConstantList list; auto ifaces = decl->interfaces; - // Length - list.push_back(DtoConstUlong(ifaces.length)); - // Protocols for(size_t i = 0; i < ifaces.length; i++) { if (auto iface = ifaces.ptr[i]) { @@ -369,7 +366,8 @@ LLConstant *ObjcClasslike::emitProtocolList() { return ObjcObject::emitList( module, - list + list, + true ); } @@ -678,7 +676,6 @@ LLConstant *ObjcClass::get() { // void ObjcProtocol::emitTable(LLGlobalVariable *table) { - size_t allocSize = getTypeAllocSize(getObjcProtocolType(module)); LLConstantList members; LLGlobalVariable *protocolList = nullptr; LLGlobalVariable *classMethodList = nullptr; @@ -715,10 +712,12 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { optInstanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false, true), optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); optInstanceMethodList->setInitializer(optInstanceMethodConsts); } - + + auto protoType = ObjcProtocol::getObjcProtocolType(module); + auto allocSize = getTypeAllocSize(protoType); members.push_back(getNullPtr()); // isa members.push_back(wrapNull(protocolList)); // protocols - members.push_back(emitName()); // mangledName + members.push_back(this->emitName()); // mangledName members.push_back(wrapNull(instanceMethodList)); // instanceMethods members.push_back(wrapNull(classMethodList)); // classMethods members.push_back(wrapNull(optInstanceMethodList)); // optionalInstanceMethods @@ -728,7 +727,7 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { members.push_back(DtoConstUint(0)); // flags table->setInitializer(LLConstantStruct::get( - getObjcProtocolType(module), + protoType, members )); } @@ -740,17 +739,17 @@ LLConstant *ObjcProtocol::emit() { auto name = getName(); auto protoName = getObjcProtoSymbol(name); auto protoLabel = getObjcProtoLabelSymbol(name); + auto protoType = ObjcProtocol::getObjcProtocolType(module); // We want it to be locally hidden and weak since the protocols // may be declared in multiple object files. - protocolTable = getOrCreateWeak(protoName, ObjcProtocol::getObjcProtocolType(module), OBJC_SECNAME_DATA); + protocolTable = getOrCreateWeak(protoName, protoType, OBJC_SECNAME_DATA); protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOREFS); protoref->setInitializer(protocolTable); // Emit their structure. this->scan(); - this->emitName(); this->emitTable(protocolTable); this->retain(protocolTable); diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index eeaeb219817..c650596751f 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -786,7 +786,7 @@ LLType *getI32Type() { return LLType::getInt32Ty(gIR->context()); } LLType *getI64Type() { return LLType::getInt64Ty(gIR->context()); } -LLType *getSizeTType() { return LLType::getIntNTy(gIR->context(), getPointerSizeInBits()); } +LLType *getSizeTType() { return DtoSize_t(); } LLPointerType *getOpaquePtrType(unsigned addressSpace) { return LLPointerType::get(gIR->context(), addressSpace); From 9b32dc8faedfd0945721c677c0f0993da6bb945a Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 12:24:20 +0100 Subject: [PATCH 36/67] More codegen robustness --- gen/objcgen.cpp | 50 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 22454302038..f5b20a2afed 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -390,6 +390,10 @@ LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods, bool } } + // List is empty too, but due to optionals not matching. + if (toAdd.empty()) + return nullptr; + return ObjcObject::emitList( module, toAdd @@ -447,29 +451,19 @@ void ObjcClass::onScan(bool meta) { } LLConstant *ObjcClass::emitIvarList() { - LLConstantList members; - - // Size of ivar_t - members.push_back(DtoConstUint( - getTypeAllocSize(ObjcIvar::getObjcIvarType(module)) - )); - - // Ivar count - members.push_back(DtoConstUint( - ivars.size() - )); + LLConstantList ivarList; // Push on all the ivars. for(size_t i = 0; i < ivars.size(); i++) { auto ivarInfo = ivars[i]->info(); if (ivarInfo) - members.push_back(ivarInfo); + ivarList.push_back(ivarInfo); } - return LLConstantStruct::getAnon( - members, - true + return this->emitList( + module, + ivarList ); } @@ -562,14 +556,16 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { if (!meta) { // Base Protocols - auto baseProtocols = emitProtocolList(); - protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); - protocolList->setInitializer(baseProtocols); + if (auto baseProtocols = emitProtocolList()) { + protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); + protocolList->setInitializer(baseProtocols); + } // Instance variables - auto baseIvars = emitIvarList(); - ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); - ivarList->setInitializer(baseIvars); + if (auto baseIvars = emitIvarList()) { + ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); + ivarList->setInitializer(baseIvars); + } } @@ -578,7 +574,7 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { members.push_back(DtoConstUint(getInstanceStart(meta))); members.push_back(DtoConstUint(getInstanceSize(meta))); members.push_back(getNullPtr()); - members.push_back(emitName()); + members.push_back(this->emitName()); members.push_back(wrapNull(methodList)); members.push_back(wrapNull(protocolList)); members.push_back(wrapNull(ivarList)); @@ -615,10 +611,11 @@ LLConstant *ObjcClass::emit() { metaClassTable = makeGlobal(metaName, ObjcClass::getObjcClassType(module), "", true, false); // Still emit ivars. - auto baseIvars = emitIvarList(); - auto ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); - ivarList->setInitializer(baseIvars); - this->retain(ivarList); + if (auto baseIvars = emitIvarList()) { + auto ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); + ivarList->setInitializer(baseIvars); + this->retain(ivarList); + } return classTable; } @@ -747,7 +744,6 @@ LLConstant *ObjcProtocol::emit() { protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOREFS); protoref->setInitializer(protocolTable); - // Emit their structure. this->scan(); this->emitTable(protocolTable); From 19189ea37793ee08fc5d959c1eb061a9cf371059 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Mon, 25 Nov 2024 17:10:37 +0100 Subject: [PATCH 37/67] emit protocol table as const --- gen/objcgen.cpp | 57 +++++++++++++++++++++++++++---------------------- gen/objcgen.h | 2 +- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index f5b20a2afed..9f949078b57 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -336,14 +336,6 @@ void ObjcClasslike::onScan(bool meta) { } } -bool ifaceListHas(ObjcList &list, InterfaceDeclaration *curr) { - for(auto it = list.begin(); it != list.end(); ++it) { - if (*it == curr) - return true; - } - return false; -} - LLConstant *ObjcClasslike::emitProtocolList() { LLConstantList list; auto ifaces = decl->interfaces; @@ -672,7 +664,7 @@ LLConstant *ObjcClass::get() { // PROTOCOLS // -void ObjcProtocol::emitTable(LLGlobalVariable *table) { +LLConstant *ObjcProtocol::emitTable() { LLConstantList members; LLGlobalVariable *protocolList = nullptr; LLGlobalVariable *classMethodList = nullptr; @@ -680,37 +672,52 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { LLGlobalVariable *optClassMethodList = nullptr; LLGlobalVariable *optInstanceMethodList = nullptr; + this->scan(); + // Base Protocols if (auto baseProtocols = this->emitProtocolList()) { - protocolList = getOrCreateWeak(getObjcProtoListSymbol(getName(), true), baseProtocols->getType(), OBJC_SECNAME_CONST); + auto sym = getObjcProtoListSymbol(getName(), true); + protocolList = getOrCreateWeak(sym, baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); } // Class methods if (auto classMethodConsts = this->emitMethodList(classMethods)) { - classMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true, false), classMethodConsts->getType(), OBJC_SECNAME_CONST); + auto sym = getObjcProtoMethodListSymbol(getName(), true, false); + classMethodList = makeGlobal(sym, classMethodConsts->getType(), OBJC_SECNAME_CONST); classMethodList->setInitializer(classMethodConsts); + classMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + classMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } // Optional class methods if (auto optClassMethodConsts = this->emitMethodList(classMethods, true)) { - optClassMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), true, true), optClassMethodConsts->getType(), OBJC_SECNAME_CONST); + auto sym = getObjcProtoMethodListSymbol(getName(), true, true); + optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST); optClassMethodList->setInitializer(optClassMethodConsts); + optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } // Instance methods if (auto instanceMethodConsts = this->emitMethodList(instanceMethods)) { - instanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false, false), instanceMethodConsts->getType(), OBJC_SECNAME_CONST); + auto sym = getObjcProtoMethodListSymbol(getName(), false, false); + instanceMethodList = makeGlobal(sym, instanceMethodConsts->getType(), OBJC_SECNAME_CONST); instanceMethodList->setInitializer(instanceMethodConsts); + instanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + instanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } // Optional instance methods if (auto optInstanceMethodConsts = this->emitMethodList(instanceMethods, true)) { - optInstanceMethodList = getOrCreateWeak(getObjcProtoMethodListSymbol(getName(), false, true), optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); + auto sym = getObjcProtoMethodListSymbol(getName(), false, true); + optInstanceMethodList = makeGlobal(sym, optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); optInstanceMethodList->setInitializer(optInstanceMethodConsts); + optInstanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + optInstanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } - auto protoType = ObjcProtocol::getObjcProtocolType(module); + auto protoType = getObjcProtocolType(module); auto allocSize = getTypeAllocSize(protoType); members.push_back(getNullPtr()); // isa members.push_back(wrapNull(protocolList)); // protocols @@ -722,11 +729,11 @@ void ObjcProtocol::emitTable(LLGlobalVariable *table) { members.push_back(getNullPtr()); // instanceProperties (TODO) members.push_back(DtoConstUint(allocSize)); // size members.push_back(DtoConstUint(0)); // flags - - table->setInitializer(LLConstantStruct::get( + + return LLConstantStruct::get( protoType, members - )); + ); } LLConstant *ObjcProtocol::emit() { @@ -736,18 +743,18 @@ LLConstant *ObjcProtocol::emit() { auto name = getName(); auto protoName = getObjcProtoSymbol(name); auto protoLabel = getObjcProtoLabelSymbol(name); - auto protoType = ObjcProtocol::getObjcProtocolType(module); + + // Emit their structure. + auto protoTableConst = this->emitTable(); // We want it to be locally hidden and weak since the protocols // may be declared in multiple object files. - protocolTable = getOrCreateWeak(protoName, protoType, OBJC_SECNAME_DATA); + protocolTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); + protocolTable->setInitializer(protoTableConst); + protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOREFS); protoref->setInitializer(protocolTable); - // Emit their structure. - this->scan(); - this->emitTable(protocolTable); - this->retain(protocolTable); this->retain(protoref); return protocolTable; @@ -777,7 +784,7 @@ ObjcClass *ObjCState::getClassRef(ClassDeclaration *cd) { } ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { - + auto protoList = this->protocols; if (!protoList.empty()) { for(auto it : protoList) { diff --git a/gen/objcgen.h b/gen/objcgen.h index 40e209f4bdf..c88168e12a3 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -371,7 +371,7 @@ class ObjcProtocol : public ObjcClasslike { LLConstant *emit() override; // Emits the protocol table. - void emitTable(LLGlobalVariable *table); + LLConstant *emitTable(); private: LLGlobalVariable *protoref; From 091bdb2caff9e102862923ebdd6d5d641bd497b2 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 03:11:01 +0100 Subject: [PATCH 38/67] Make protocol table anon struct --- gen/objcgen.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 9f949078b57..c7d5c04f0d8 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -719,6 +719,7 @@ LLConstant *ObjcProtocol::emitTable() { auto protoType = getObjcProtocolType(module); auto allocSize = getTypeAllocSize(protoType); + members.push_back(getNullPtr()); // isa members.push_back(wrapNull(protocolList)); // protocols members.push_back(this->emitName()); // mangledName @@ -730,9 +731,9 @@ LLConstant *ObjcProtocol::emitTable() { members.push_back(DtoConstUint(allocSize)); // size members.push_back(DtoConstUint(0)); // flags - return LLConstantStruct::get( - protoType, - members + return LLConstantStruct::getAnon( + members, + true ); } From d252845e1a3c44deae009687075572907e822d4f Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 04:06:21 +0100 Subject: [PATCH 39/67] Fix callable type, generate protocol_list_t properly. --- gen/objcgen.cpp | 33 ++++++++++++++++++++++++++++++--- gen/objcgen.h | 10 +++++++--- gen/tocall.cpp | 3 +++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index c7d5c04f0d8..d3b1d55e7f9 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -189,7 +189,7 @@ LLConstant *offsetIvar(size_t ivaroffset) { return DtoConstUint(getPointerSize()+ivaroffset); } -LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, bool isCountPtrSized) { +LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, bool alignSizeT) { LLConstantList members; // Emit nullptr for empty lists. @@ -199,7 +199,7 @@ LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, b // Size of stored struct. size_t allocSize = getTypeAllocSize(objects.front()->getType()); members.push_back( - isCountPtrSized ? + alignSizeT ? DtoConstSize_t(allocSize) : DtoConstUint(allocSize) ); @@ -222,6 +222,33 @@ LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, b ); } +LLConstant *ObjcObject::emitCountList(llvm::Module &module, LLConstantList objects, bool alignSizeT) { + LLConstantList members; + + // Emit nullptr for empty lists. + if (objects.empty()) + return nullptr; + + // Method count + members.push_back( + alignSizeT ? + DtoConstSize_t(objects.size()) : + DtoConstUint(objects.size()) + ); + + // Insert all the objects in the constant list. + members.insert( + members.end(), + objects.begin(), + objects.end() + ); + + return LLConstantStruct::getAnon( + members, + true + ); +} + // // METHODS @@ -356,7 +383,7 @@ LLConstant *ObjcClasslike::emitProtocolList() { } } - return ObjcObject::emitList( + return ObjcObject::emitCountList( module, list, true diff --git a/gen/objcgen.h b/gen/objcgen.h index c88168e12a3..ddd97c83a46 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -104,9 +104,6 @@ class ObjcObject { // Gets the name of the object. virtual const char *getName() { return nullptr; } - // Emits a new list for the specified objects as a constant. - static LLConstant *emitList(llvm::Module &module, LLConstantList objects, bool isCountPtrSized = false); - protected: // Gets a global variable or creates it. @@ -139,6 +136,13 @@ class ObjcObject { // Called to emit the data for the type. virtual LLConstant *emit() { return nullptr; } + // Emits a new list for the specified objects as a constant. + static LLConstant *emitList(llvm::Module &module, LLConstantList objects, bool alignSizeT = false); + + // Emits a new list for the specified objects as a constant. + // This list only features count and not struct size. + static LLConstant *emitCountList(llvm::Module &module, LLConstantList objects, bool alignSizeT = false); + // Retains a symbol. void retain(LLGlobalVariable *toRetain); }; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index f1d30f0b309..9d41bea7488 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -907,9 +907,12 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, } if (irFty.arg_objcSelector) { + // Use runtime msgSend function bitcasted as original call const char *msgSend = gABI->objcMsgSendFunc(resulttype, irFty, directcall); + auto t = callable->getType(); callable = getRuntimeFunction(loc, gIR->module, msgSend); + callable = DtoBitCast(callable, t); } // call the function From ed01dbcd7b4c4e30c6ccda37b460ff8ecda1fdb3 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 05:07:18 +0100 Subject: [PATCH 40/67] Cast vthis to argtype --- gen/tocall.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 9d41bea7488..ce5586e8d3a 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -712,6 +712,7 @@ class ImplicitArgumentsBuilder { return; size_t index = args.size(); + auto argtype = *(llArgTypesBegin + index); if (dfnval && (dfnval->func->ident == Id::ensure || dfnval->func->ident == Id::require)) { @@ -740,10 +741,12 @@ class ImplicitArgumentsBuilder { if (auto parentfd = dfnval->func->isFuncDeclaration()) { if (auto cls = parentfd->parent->isClassDeclaration()) { + // Cast the "this" pointer to the arg type. + // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( - dfnval->vthis, - gIR->objc.getClassRef(cls)->ref(), + DtoBitCast(dfnval->vthis, argtype), + DtoLoad(getOpaquePtrType(), gIR->objc.getClassRef(cls)->ref()), "super" ); From 47638ed15cb724682f27e6447209e4e98b98a69a Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 05:58:01 +0100 Subject: [PATCH 41/67] Handle protorefs and classrefs properly --- gen/classes.cpp | 6 +++--- gen/objcgen.cpp | 28 +++++++++++++++++++++++----- gen/objcgen.h | 8 +++++--- gen/tocall.cpp | 2 +- gen/toir.cpp | 24 +++++++++++++----------- 5 files changed, 45 insertions(+), 23 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index bdca91098c6..c4a6371b961 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -365,9 +365,9 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { LLValue *objTy = getNullPtr(); if (auto chndl = _to->isClassHandle()) { if (auto ihndl = chndl->isInterfaceDeclaration()) - objTy = gIR->objc.getProtocolRef(ihndl)->ref(); + objTy = gIR->objc.getProtocolRef(ihndl)->ref(getOpaquePtrType()); else - objTy = gIR->objc.getClassRef(chndl)->ref(); + objTy = gIR->objc.getClassRef(chndl)->ref(getOpaquePtrType()); } // objc_opt_isKindOfClass will check if id is null @@ -427,7 +427,7 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // Get prototype_t handle LLValue *protoTy = getNullPtr(); if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) { - protoTy = gIR->objc.getProtocolRef(ifhndl)->ref(); + protoTy = gIR->objc.getProtocolRef(ifhndl)->ref(getOpaquePtrType()); } // Class && kindOfProtocolFunc(Class) ? id : null diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index d3b1d55e7f9..215c0933f49 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -131,6 +131,10 @@ std::string getObjcClassSymbol(const char *name, bool meta) { return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", name); } +std::string getObjcClassLabelSymbol(const char *name) { + return getObjcSymbolName("_OBJC_LABEL_CLASS_$_", name); +} + std::string getObjcClassMethodListSymbol(const char *className, bool meta) { return getObjcSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className); } @@ -636,6 +640,9 @@ LLConstant *ObjcClass::emit() { this->retain(ivarList); } + // Still emit classref + classref = getOrCreateWeak(getObjcClassLabelSymbol(name), getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); + classref->setInitializer(classTable); return classTable; } @@ -649,6 +656,10 @@ LLConstant *ObjcClass::emit() { classRoTable = getOrCreate(classNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); metaClassRoTable = getOrCreate(metaNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); + + classref = getOrCreateWeak(getObjcClassLabelSymbol(name), getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); + classref->setInitializer(classTable); + this->scan(); // Emit their structure. @@ -662,20 +673,24 @@ LLConstant *ObjcClass::emit() { this->retain(metaClassTable); this->retain(classRoTable); this->retain(metaClassRoTable); + this->retain(classref); return classTable; } -LLValue *ObjcClass::deref(LLValue *classptr) { +LLValue *ObjcClass::deref(LLValue *classptr, LLType *as) { if (decl->objc.isExtern && decl->objc.isSwiftStub) { auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); - return gIR->CreateCallOrInvoke(loadClassFunc, classptr, ""); + return DtoBitCast( + gIR->CreateCallOrInvoke(loadClassFunc, classptr, ""), + as + ); } - return classptr; + return DtoLoad(as, classptr); } -LLValue *ObjcClass::ref() { - return deref(classTable); +LLValue *ObjcClass::ref(LLType *as) { + return deref(classref, as); } LLConstant *ObjcClass::get() { @@ -788,6 +803,9 @@ LLConstant *ObjcProtocol::emit() { return protocolTable; } +LLValue *ObjcProtocol::ref(LLType *as) { + return DtoLoad(as, protoref); +} // // STATE diff --git a/gen/objcgen.h b/gen/objcgen.h index ddd97c83a46..184ffd30083 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -80,6 +80,7 @@ bool objc_isSupported(const llvm::Triple &triple); // Generate name strings std::string getObjcClassRoSymbol(const char *name, bool meta); std::string getObjcClassSymbol(const char *name, bool meta); +std::string getObjcClassLabelSymbol(const char *name); std::string getObjcClassMethodListSymbol(const char *className, bool meta); std::string getObjcIvarListSymbol(const char *className); std::string getObjcIvarSymbol(const char *className, const char *varName); @@ -367,7 +368,7 @@ class ObjcProtocol : public ObjcClasslike { } // Gets the protocol ref. - LLConstant *ref() { return protoref; } + LLValue *ref(LLType *as); protected: @@ -471,7 +472,7 @@ class ObjcClass : public ObjcClasslike { LLGlobalVariable *getIVarOffset(VarDeclaration *vd); // Gets a reference to the class. - LLValue *ref(); + LLValue *ref(LLType *as); // Gets the main reference to the object. LLConstant *get() override; @@ -502,7 +503,7 @@ class ObjcClass : public ObjcClasslike { // instance variables LLConstant *emitIvarList(); - LLValue *deref(LLValue *classptr); + LLValue *deref(LLValue *classptr, LLType *as); // Gets the empty cache variable, and creates a reference to it // if needed. @@ -513,6 +514,7 @@ class ObjcClass : public ObjcClasslike { return objcCache; } + LLGlobalVariable *classref; LLGlobalVariable *classTable; LLGlobalVariable *classRoTable; LLGlobalVariable *metaClassTable; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index ce5586e8d3a..6939d954c5c 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -746,7 +746,7 @@ class ImplicitArgumentsBuilder { // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( DtoBitCast(dfnval->vthis, argtype), - DtoLoad(getOpaquePtrType(), gIR->objc.getClassRef(cls)->ref()), + gIR->objc.getClassRef(cls)->ref(getOpaquePtrType()), "super" ); diff --git a/gen/toir.cpp b/gen/toir.cpp index b08e990d0e2..2f711947bf5 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -334,20 +334,22 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; - // Protocols + auto loadtype = DtoType(e->type); + if (auto iface = e->classDeclaration->isInterfaceDeclaration()) { - auto proto = gIR->objc.getProtocolRef(iface); - auto loaded = proto->get(); - result = new DImValue(e->type, loaded); - return; - } + // Protocols + if (auto proto = gIR->objc.getProtocolRef(iface)) { + result = new DImValue(e->type, proto->ref(loadtype)); + return; + } + } else { - // Classes - if (auto klass = gIR->objc.getClassRef(e->classDeclaration)) { - auto loaded = klass->ref(); - result = new DImValue(e->type, loaded); - return; + // Classes + if (auto klass = gIR->objc.getClassRef(e->classDeclaration)) { + result = new DImValue(e->type, klass->ref(loadtype)); + return; + } } llvm_unreachable("Unknown type for ObjcClassReferenceExp."); From 9c638114fdabf0db757de9d586cdfbe9a6b9e8fc Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 06:25:59 +0100 Subject: [PATCH 42/67] seperate label ref and deref --- gen/classes.cpp | 6 +++--- gen/objcgen.cpp | 19 ++++++++++++------- gen/objcgen.h | 14 +++++++++----- gen/tocall.cpp | 2 +- gen/toir.cpp | 4 ++-- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index c4a6371b961..aaa1e6484a6 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -365,9 +365,9 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { LLValue *objTy = getNullPtr(); if (auto chndl = _to->isClassHandle()) { if (auto ihndl = chndl->isInterfaceDeclaration()) - objTy = gIR->objc.getProtocolRef(ihndl)->ref(getOpaquePtrType()); + objTy = gIR->objc.getProtocolRef(ihndl)->deref(getOpaquePtrType()); else - objTy = gIR->objc.getClassRef(chndl)->ref(getOpaquePtrType()); + objTy = gIR->objc.getClassRef(chndl)->deref(getOpaquePtrType()); } // objc_opt_isKindOfClass will check if id is null @@ -427,7 +427,7 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // Get prototype_t handle LLValue *protoTy = getNullPtr(); if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) { - protoTy = gIR->objc.getProtocolRef(ifhndl)->ref(getOpaquePtrType()); + protoTy = gIR->objc.getProtocolRef(ifhndl)->deref(getOpaquePtrType()); } // Class && kindOfProtocolFunc(Class) ? id : null diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 215c0933f49..738a61bdf2a 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -380,7 +380,7 @@ LLConstant *ObjcClasslike::emitProtocolList() { if (auto ifacesym = (InterfaceDeclaration *)iface->sym) { if (ifacesym->classKind == ClassKind::objc) { if (auto proto = this->objc.getProtocolRef(ifacesym)) { - list.push_back(proto->get()); + list.push_back(proto->ref()); } } } @@ -677,20 +677,20 @@ LLConstant *ObjcClass::emit() { return classTable; } -LLValue *ObjcClass::deref(LLValue *classptr, LLType *as) { +LLValue *ObjcClass::deref(LLType *as) { if (decl->objc.isExtern && decl->objc.isSwiftStub) { auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); return DtoBitCast( - gIR->CreateCallOrInvoke(loadClassFunc, classptr, ""), + gIR->CreateCallOrInvoke(loadClassFunc, classref, ""), as ); } - return DtoLoad(as, classptr); + return DtoLoad(as, classref); } -LLValue *ObjcClass::ref(LLType *as) { - return deref(classref, as); +LLConstant *ObjcClass::ref() { + return classref; } LLConstant *ObjcClass::get() { @@ -803,10 +803,15 @@ LLConstant *ObjcProtocol::emit() { return protocolTable; } -LLValue *ObjcProtocol::ref(LLType *as) { +LLValue *ObjcProtocol::deref(LLType *as) { return DtoLoad(as, protoref); } + +LLConstant *ObjcProtocol::ref() { + return protoref; +} + // // STATE // diff --git a/gen/objcgen.h b/gen/objcgen.h index 184ffd30083..2858e73d817 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -368,7 +368,10 @@ class ObjcProtocol : public ObjcClasslike { } // Gets the protocol ref. - LLValue *ref(LLType *as); + LLConstant *ref(); + + // Gets the protocol ref. + LLValue *deref(LLType *as); protected: @@ -471,8 +474,11 @@ class ObjcClass : public ObjcClasslike { LLGlobalVariable *getIVarOffset(VarDeclaration *vd); - // Gets a reference to the class. - LLValue *ref(LLType *as); + // Gets the class ref. + LLConstant *ref(); + + // Dereferences the class. + LLValue *deref(LLType *as); // Gets the main reference to the object. LLConstant *get() override; @@ -503,8 +509,6 @@ class ObjcClass : public ObjcClasslike { // instance variables LLConstant *emitIvarList(); - LLValue *deref(LLValue *classptr, LLType *as); - // Gets the empty cache variable, and creates a reference to it // if needed. LLGlobalVariable *getEmptyCache() { diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 6939d954c5c..8c42a6e6a0e 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -746,7 +746,7 @@ class ImplicitArgumentsBuilder { // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( DtoBitCast(dfnval->vthis, argtype), - gIR->objc.getClassRef(cls)->ref(getOpaquePtrType()), + gIR->objc.getClassRef(cls)->deref(getOpaquePtrType()), "super" ); diff --git a/gen/toir.cpp b/gen/toir.cpp index 2f711947bf5..f410bcd016e 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -340,14 +340,14 @@ class ToElemVisitor : public Visitor { // Protocols if (auto proto = gIR->objc.getProtocolRef(iface)) { - result = new DImValue(e->type, proto->ref(loadtype)); + result = new DImValue(e->type, proto->deref(loadtype)); return; } } else { // Classes if (auto klass = gIR->objc.getClassRef(e->classDeclaration)) { - result = new DImValue(e->type, klass->ref(loadtype)); + result = new DImValue(e->type, klass->deref(loadtype)); return; } } From 26060733e91c74e9d06328f2e3475f0fb61a7e52 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 07:07:26 +0100 Subject: [PATCH 43/67] Fix method lookup --- gen/objcgen.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 738a61bdf2a..3503674abdf 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -862,7 +862,7 @@ ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { if (!method && id->baseClass) { method = getMethodRef(id->baseClass, fd); } - return proto->getMethod(fd); + return method; } } @@ -883,11 +883,10 @@ ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { } ObjcMethod *ObjCState::getMethodRef(FuncDeclaration *fd) { - if (auto cd = fd->parent->isClassDeclaration()) - return getMethodRef(cd, fd); - - if (auto id = fd->parent->isInterfaceDeclaration()) - return getMethodRef(id, fd); + if (auto cd = fd->parent->isClassDeclaration()) { + if (auto retval = getMethodRef(cd, fd)) + return retval; + } return nullptr; } From b561bd416f918e1739f69766f29172689e546a67 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 07:24:50 +0100 Subject: [PATCH 44/67] Enable objective-c tests --- tests/dmd/runnable/objc_call.d | 1 - tests/dmd/runnable/objc_class.d | 1 - tests/dmd/runnable/objc_external_class_19700.d | 1 - tests/dmd/runnable/objc_instance_variable.d | 1 - tests/dmd/runnable/objc_objc_msgSend.d | 1 - tests/dmd/runnable/objc_protocol.d | 1 - tests/dmd/runnable/objc_protocol_sections.d | 1 - tests/dmd/runnable/objc_super_call.d | 1 - 8 files changed, 8 deletions(-) diff --git a/tests/dmd/runnable/objc_call.d b/tests/dmd/runnable/objc_call.d index 481ee75d841..98199a0abb4 100644 --- a/tests/dmd/runnable/objc_call.d +++ b/tests/dmd/runnable/objc_call.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: // REQUIRED_ARGS: -L-framework -LFoundation diff --git a/tests/dmd/runnable/objc_class.d b/tests/dmd/runnable/objc_class.d index 7ca0fbfe5b4..e66528ee6a9 100644 --- a/tests/dmd/runnable/objc_class.d +++ b/tests/dmd/runnable/objc_class.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: objc_class.m // REQUIRED_ARGS: -L-framework -LFoundation diff --git a/tests/dmd/runnable/objc_external_class_19700.d b/tests/dmd/runnable/objc_external_class_19700.d index ff206fa2708..074cb2cc05b 100644 --- a/tests/dmd/runnable/objc_external_class_19700.d +++ b/tests/dmd/runnable/objc_external_class_19700.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: objc_instance_variable.m // REQUIRED_ARGS: -L-framework -LFoundation diff --git a/tests/dmd/runnable/objc_instance_variable.d b/tests/dmd/runnable/objc_instance_variable.d index 63e7e0581cb..9e2ce1075cb 100644 --- a/tests/dmd/runnable/objc_instance_variable.d +++ b/tests/dmd/runnable/objc_instance_variable.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: objc_instance_variable.m // REQUIRED_ARGS: -L-framework -LFoundation diff --git a/tests/dmd/runnable/objc_objc_msgSend.d b/tests/dmd/runnable/objc_objc_msgSend.d index ef8e5b09dbe..32f8574f17f 100644 --- a/tests/dmd/runnable/objc_objc_msgSend.d +++ b/tests/dmd/runnable/objc_objc_msgSend.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: objc_objc_msgSend.m // REQUIRED_ARGS: -L-framework -LFoundation diff --git a/tests/dmd/runnable/objc_protocol.d b/tests/dmd/runnable/objc_protocol.d index 18102e83a75..3cac51e1c97 100644 --- a/tests/dmd/runnable/objc_protocol.d +++ b/tests/dmd/runnable/objc_protocol.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: objc_protocol.m // REQUIRED_ARGS: -L-lobjc diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index 1382bf2569f..89790c1cfc7 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: // REQUIRED_ARGS: -L-lobjc diff --git a/tests/dmd/runnable/objc_super_call.d b/tests/dmd/runnable/objc_super_call.d index 7fc139a23c8..f0926cb2403 100644 --- a/tests/dmd/runnable/objc_super_call.d +++ b/tests/dmd/runnable/objc_super_call.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: objc_super_call.m // REQUIRED_ARGS: -L-framework -LFoundation From d106402a14eb978f9cc02ba878ac70c62710bcf7 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 07:27:54 +0100 Subject: [PATCH 45/67] Enable objc_call_static test --- tests/dmd/runnable/objc_call_static.d | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/dmd/runnable/objc_call_static.d b/tests/dmd/runnable/objc_call_static.d index c4f774dacb6..2c51413cc6b 100644 --- a/tests/dmd/runnable/objc_call_static.d +++ b/tests/dmd/runnable/objc_call_static.d @@ -1,5 +1,3 @@ -// LDC: not implemented yet (issue #2670) -// DISABLED: LDC // EXTRA_OBJC_SOURCES: // REQUIRED_ARGS: -L-framework -LFoundation From 9690109d8db9379af24de72411b85221dce3face Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 08:08:46 +0100 Subject: [PATCH 46/67] Scan both classes and protocols for method ref --- gen/objcgen.cpp | 22 +++++++++++++++------- gen/tocall.cpp | 2 -- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 3503674abdf..a370019de6b 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -859,21 +859,29 @@ ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { // Attempt to get the method, if not found // try the parent. auto method = proto->getMethod(fd); - if (!method && id->baseClass) { - method = getMethodRef(id->baseClass, fd); + if (!method) { + for (auto baseclass : *id->baseclasses) { + method = getMethodRef(baseclass->sym, fd); + + if (method) + break; + } } return method; } - } - - if (auto klass = getClassRef(cd)) { + } else if (auto klass = getClassRef(cd)) { klass->scan(); // Attempt to get the method, if not found // try the parent. auto method = klass->getMethod(fd); - if (!method && cd->baseClass) { - method = getMethodRef(cd->baseClass, fd); + if (!method) { + for (auto baseclass : *id->baseclasses) { + method = getMethodRef(baseclass->sym, fd); + + if (method) + break; + } } return method; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 8c42a6e6a0e..0e9a2eafe78 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -741,8 +741,6 @@ class ImplicitArgumentsBuilder { if (auto parentfd = dfnval->func->isFuncDeclaration()) { if (auto cls = parentfd->parent->isClassDeclaration()) { - // Cast the "this" pointer to the arg type. - // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( DtoBitCast(dfnval->vthis, argtype), From 9c4700af6834976a7503db6855da65bcf4f14c8a Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 08:15:20 +0100 Subject: [PATCH 47/67] Enable objective-c tests on arm as well. --- tests/dmd/run.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/dmd/run.d b/tests/dmd/run.d index 1ef15b82e6b..2d9e43bb027 100755 --- a/tests/dmd/run.d +++ b/tests/dmd/run.d @@ -619,8 +619,7 @@ string[string] getEnvironment() } version(OSX) - version(X86_64) - env["D_OBJC"] = "1"; + env["D_OBJC"] = "1"; } return env; } From 05ab5676d7cbfb6ce51585049dbbe5147cc9d1fd Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 08:31:10 +0100 Subject: [PATCH 48/67] supress objc linker warning in tests --- tests/dmd/runnable/objc_call.d | 2 +- tests/dmd/runnable/objc_class.d | 2 +- tests/dmd/runnable/objc_external_class_19700.d | 2 +- tests/dmd/runnable/objc_instance_variable.d | 2 +- tests/dmd/runnable/objc_objc_msgSend.d | 2 +- tests/dmd/runnable/objc_protocol.d | 2 +- tests/dmd/runnable/objc_protocol_sections.d | 2 +- tests/dmd/runnable/objc_self_test.d | 2 +- tests/dmd/runnable/objc_super_call.d | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/dmd/runnable/objc_call.d b/tests/dmd/runnable/objc_call.d index 98199a0abb4..9c8335ef89a 100644 --- a/tests/dmd/runnable/objc_call.d +++ b/tests/dmd/runnable/objc_call.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_class.d b/tests/dmd/runnable/objc_class.d index e66528ee6a9..84a930cef6a 100644 --- a/tests/dmd/runnable/objc_class.d +++ b/tests/dmd/runnable/objc_class.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_class.m -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_external_class_19700.d b/tests/dmd/runnable/objc_external_class_19700.d index 074cb2cc05b..3e3732a624a 100644 --- a/tests/dmd/runnable/objc_external_class_19700.d +++ b/tests/dmd/runnable/objc_external_class_19700.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_instance_variable.m -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_instance_variable.d b/tests/dmd/runnable/objc_instance_variable.d index 9e2ce1075cb..fbbe4600013 100644 --- a/tests/dmd/runnable/objc_instance_variable.d +++ b/tests/dmd/runnable/objc_instance_variable.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_instance_variable.m -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_objc_msgSend.d b/tests/dmd/runnable/objc_objc_msgSend.d index 32f8574f17f..5b364d74baa 100644 --- a/tests/dmd/runnable/objc_objc_msgSend.d +++ b/tests/dmd/runnable/objc_objc_msgSend.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_objc_msgSend.m -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_protocol.d b/tests/dmd/runnable/objc_protocol.d index 3cac51e1c97..c6cd7d80d64 100644 --- a/tests/dmd/runnable/objc_protocol.d +++ b/tests/dmd/runnable/objc_protocol.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_protocol.m -// REQUIRED_ARGS: -L-lobjc +// REQUIRED_ARGS: -L-lobjc -L-w import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index 89790c1cfc7..d3d68fef063 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: -// REQUIRED_ARGS: -L-lobjc +// REQUIRED_ARGS: -L-lobjc -L-w // This file verifies that Objective-C protocols are put in the correct segments // and sections in the binary. If not, functions from the Objective-C runtime diff --git a/tests/dmd/runnable/objc_self_test.d b/tests/dmd/runnable/objc_self_test.d index 407d80faed2..8747ec091f8 100644 --- a/tests/dmd/runnable/objc_self_test.d +++ b/tests/dmd/runnable/objc_self_test.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_self_test.m -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w extern (C) int getValue(); diff --git a/tests/dmd/runnable/objc_super_call.d b/tests/dmd/runnable/objc_super_call.d index f0926cb2403..892c8e0760c 100644 --- a/tests/dmd/runnable/objc_super_call.d +++ b/tests/dmd/runnable/objc_super_call.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_super_call.m -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; From f9fd9772d0212fb989c4d1fc3348c9a25dde8257 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 10:15:05 +0100 Subject: [PATCH 49/67] Fix class and protocol gen structure --- gen/objcgen.cpp | 146 ++++++++++++++++---------- gen/objcgen.h | 12 +-- tests/dmd/runnable/objc_call_static.d | 2 +- 3 files changed, 99 insertions(+), 61 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index a370019de6b..ac139218552 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -141,7 +141,7 @@ std::string getObjcClassMethodListSymbol(const char *className, bool meta) { std::string getObjcProtoMethodListSymbol(const char *className, bool meta, bool optional) { return optional ? - getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_OPTIONAL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_OPTIONAL_INSTANCE_METHODS_", className) : + getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_", className) : getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className); } @@ -154,7 +154,7 @@ std::string getObjcProtoSymbol(const char *name) { } std::string getObjcProtoListSymbol(const char *name, bool isProtocol) { - return getObjcSymbolName(isProtocol ? "_OBJC_PROTOCOL_PROTOCOLS_$_" : "_OBJC_CLASS_PROTOCOLS_$_", name); + return getObjcSymbolName(isProtocol ? "_OBJC_$_PROTOCOL_REFS_" : "_OBJC_CLASS_PROTOCOLS_$_", name); } std::string getObjcProtoLabelSymbol(const char *name) { @@ -228,10 +228,6 @@ LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, b LLConstant *ObjcObject::emitCountList(llvm::Module &module, LLConstantList objects, bool alignSizeT) { LLConstantList members; - - // Emit nullptr for empty lists. - if (objects.empty()) - return nullptr; // Method count members.push_back( @@ -241,12 +237,16 @@ LLConstant *ObjcObject::emitCountList(llvm::Module &module, LLConstantList objec ); // Insert all the objects in the constant list. - members.insert( - members.end(), - objects.begin(), - objects.end() - ); + if (!objects.empty()) { + members.insert( + members.end(), + objects.begin(), + objects.end() + ); + } + // These lists need to be null terminated. + members.push_back(getNullPtr()); return LLConstantStruct::getAnon( members, true @@ -270,17 +270,23 @@ LLConstant *ObjcMethod::emit() { } // Implements the objc_method structure -LLConstant *ObjcMethod::info() { +LLConstant *ObjcMethod::info(bool emitExtern) { if (!name) emit(); - if (!decl->fbody) + if (!emitExtern && !decl->fbody) return nullptr; auto func = DtoCallee(decl); return LLConstantStruct::get( ObjcMethod::getObjcMethodType(module), - { name, type, DtoBitCast(func, getOpaquePtrType()) } + { + name, + type, + decl->fbody ? + DtoBitCast(func, getOpaquePtrType()) : + getNullPtr() + } ); } @@ -342,27 +348,61 @@ LLConstant *ObjcIvar::info() { // CLASS-LIKES // -void ObjcClasslike::onScan(bool meta) { - auto methods = - (meta && decl->objc.metaclass ) ? - decl->objc.metaclass->objc.methodList : - decl->objc.methodList; - - for(size_t i = 0; i < methods.length; i++) { - auto method = methods.ptr[i]; - - // Static functions are class methods. - if (meta && method->isStatic()) { - classMethods.push_back( - new ObjcMethod(module, objc, method) - ); - continue; +void ObjcClasslike::onScan() { + if (auto proto = decl->isInterfaceDeclaration()) { + + // Interface vtable. + for(auto vtblentry : proto->vtbl) { + if (auto method = vtblentry->isFuncDeclaration()) { + + // Static functions are class methods. + if (method->isStatic()) { + classMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } else { + instanceMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } + } + } + } else { + + // Class funcs + if (auto metaclass = decl->objc.metaclass) { + auto metamethods = metaclass->objc.methodList; + + for(size_t i = 0; i < metamethods.length; i++) { + auto method = metamethods.ptr[i]; + + // Static functions are class methods. + if (method->isStatic()) { + classMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } else { + instanceMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } + } } - if (!meta && !method->isStatic()) { - instanceMethods.push_back( - new ObjcMethod(module, objc, method) - ); + auto methods = decl->objc.methodList; + for(size_t i = 0; i < methods.length; i++) { + auto method = methods.ptr[i]; + + // Static functions are class methods. + if (method->isStatic()) { + classMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } else { + instanceMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } } } } @@ -380,7 +420,7 @@ LLConstant *ObjcClasslike::emitProtocolList() { if (auto ifacesym = (InterfaceDeclaration *)iface->sym) { if (ifacesym->classKind == ClassKind::objc) { if (auto proto = this->objc.getProtocolRef(ifacesym)) { - list.push_back(proto->ref()); + list.push_back(proto->get()); } } } @@ -401,12 +441,14 @@ LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods, bool if (methods.empty()) return nullptr; + bool isProtocol = decl->isInterfaceDeclaration(); + // Find out how many functions have actual bodies. for(size_t i = 0; i < methods.size(); i++) { auto method = methods[i]; if (method->isOptional() == optionalMethods) { - auto methodInfo = method->info(); + auto methodInfo = method->info(isProtocol); if (methodInfo) toAdd.push_back(methodInfo); @@ -459,17 +501,14 @@ size_t ObjcClass::getClassFlags(const ClassDeclaration& decl) { return flags; } -void ObjcClass::onScan(bool meta) { - ObjcClasslike::onScan(meta); - - if (!meta) { +void ObjcClass::onScan() { + ObjcClasslike::onScan(); - // Push on all the ivars. - for(size_t i = 0; i < decl->fields.size(); i++) { - auto ivar = new ObjcIvar(module, objc, decl->fields[i]); - ivars.push_back(ivar); - ivar->get(); - } + // Push on all the ivars. + for(size_t i = 0; i < decl->fields.size(); i++) { + auto ivar = new ObjcIvar(module, objc, decl->fields[i]); + ivars.push_back(ivar); + ivar->get(); } } @@ -577,12 +616,13 @@ void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { methodList->setInitializer(baseMethods); } + // Base Protocols + if (auto baseProtocols = emitProtocolList()) { + protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); + protocolList->setInitializer(baseProtocols); + } + if (!meta) { - // Base Protocols - if (auto baseProtocols = emitProtocolList()) { - protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); - protocolList->setInitializer(baseProtocols); - } // Instance variables if (auto baseIvars = emitIvarList()) { @@ -664,7 +704,7 @@ LLConstant *ObjcClass::emit() { // Emit their structure. this->emitName(); - this->emitTable(classTable, metaClassTable, getSuper(classTable), classRoTable); + this->emitTable(classTable, metaClassTable, getSuper(false), classRoTable); this->emitTable(metaClassTable, getRootMetaClass(), getSuper(true), metaClassRoTable); this->emitRoTable(classRoTable, false); this->emitRoTable(metaClassRoTable, true); @@ -763,8 +803,8 @@ LLConstant *ObjcProtocol::emitTable() { auto allocSize = getTypeAllocSize(protoType); members.push_back(getNullPtr()); // isa - members.push_back(wrapNull(protocolList)); // protocols members.push_back(this->emitName()); // mangledName + members.push_back(wrapNull(protocolList)); // protocols members.push_back(wrapNull(instanceMethodList)); // instanceMethods members.push_back(wrapNull(classMethodList)); // classMethods members.push_back(wrapNull(optInstanceMethodList)); // optionalInstanceMethods @@ -795,7 +835,7 @@ LLConstant *ObjcProtocol::emit() { protocolTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); protocolTable->setInitializer(protoTableConst); - protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOREFS); + protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST); protoref->setInitializer(protocolTable); this->retain(protocolTable); diff --git a/gen/objcgen.h b/gen/objcgen.h index 2858e73d817..877f406376c 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -178,7 +178,7 @@ class ObjcMethod : public ObjcObject { // Emits the constant struct containing the method // information. - LLConstant *info(); + LLConstant *info(bool emitExtern=false); // Gets the selector for the function LLStringRef getSelector() { @@ -291,10 +291,8 @@ class ObjcClasslike : public ObjcObject { // Scans the class-like object and fills out internal information // about functions, ivars, etc. void scan() { - if (!hasScanned) { - onScan(true); - onScan(false); - } + if (!hasScanned) + onScan(); hasScanned = true; } @@ -303,7 +301,7 @@ class ObjcClasslike : public ObjcObject { LLGlobalVariable *emitName(); // Implement this to modify scanning behaviour. - virtual void onScan(bool meta); + virtual void onScan(); // Emits a method list as a constant. LLConstant *emitMethodList(std::vector &methods, bool optionalMethods=false); @@ -494,7 +492,7 @@ class ObjcClass : public ObjcClasslike { // Called to emit the object. LLConstant *emit() override; - void onScan(bool meta) override; + void onScan() override; private: ObjcList ivars; diff --git a/tests/dmd/runnable/objc_call_static.d b/tests/dmd/runnable/objc_call_static.d index 2c51413cc6b..e8a53c6b41d 100644 --- a/tests/dmd/runnable/objc_call_static.d +++ b/tests/dmd/runnable/objc_call_static.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: -// REQUIRED_ARGS: -L-framework -LFoundation +// REQUIRED_ARGS: -L-framework -LFoundation -L-w import core.attribute : selector; From cdae5b80acf197cdc256bd6b169002baabc71beb Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 11:12:15 +0100 Subject: [PATCH 50/67] Fix objc_protocol_sections test --- tests/dmd/runnable/objc_protocol_sections.d | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index d3d68fef063..0276843f70f 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -7,7 +7,7 @@ import core.attribute : selector, optional; -extern (Objective-C): +extern(C): struct Protocol; struct objc_selector; @@ -37,6 +37,8 @@ objc_method_description protocol_getMethodDescription( Protocol* proto, SEL aSel, bool isRequiredMethod, bool isInstanceMethod ); +extern (Objective-C): + interface Foo { void foo1() @selector("foo1"); From b923a5752685d454224384efed5f7e4347924b14 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 11:17:03 +0100 Subject: [PATCH 51/67] ObjcMethod only get callee for functions with bodies --- gen/objcgen.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index ac139218552..faf32b9bb55 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -277,14 +277,13 @@ LLConstant *ObjcMethod::info(bool emitExtern) { if (!emitExtern && !decl->fbody) return nullptr; - auto func = DtoCallee(decl); return LLConstantStruct::get( ObjcMethod::getObjcMethodType(module), { name, type, decl->fbody ? - DtoBitCast(func, getOpaquePtrType()) : + DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : getNullPtr() } ); From 4d8ec8bae31cc213fbdb1cc645c937fce36877dd Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 11:52:56 +0100 Subject: [PATCH 52/67] Fix protocol class method gen --- gen/objcgen.cpp | 98 ++++++++++++++++++++----------------------------- gen/objcgen.h | 1 + 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index faf32b9bb55..9e51ce12614 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -262,6 +262,9 @@ LLConstant *ObjcMethod::emit() { name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); + llfunc = decl->fbody ? + DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : + getNullPtr(); this->retain(name); this->retain(type); @@ -279,13 +282,7 @@ LLConstant *ObjcMethod::info(bool emitExtern) { return LLConstantStruct::get( ObjcMethod::getObjcMethodType(module), - { - name, - type, - decl->fbody ? - DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : - getNullPtr() - } + { name, type, llfunc } ); } @@ -348,49 +345,13 @@ LLConstant *ObjcIvar::info() { // void ObjcClasslike::onScan() { - if (auto proto = decl->isInterfaceDeclaration()) { - - // Interface vtable. - for(auto vtblentry : proto->vtbl) { - if (auto method = vtblentry->isFuncDeclaration()) { - - // Static functions are class methods. - if (method->isStatic()) { - classMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } else { - instanceMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } - } - } - } else { - - // Class funcs - if (auto metaclass = decl->objc.metaclass) { - auto metamethods = metaclass->objc.methodList; - - for(size_t i = 0; i < metamethods.length; i++) { - auto method = metamethods.ptr[i]; - - // Static functions are class methods. - if (method->isStatic()) { - classMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } else { - instanceMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } - } - } - auto methods = decl->objc.methodList; - for(size_t i = 0; i < methods.length; i++) { - auto method = methods.ptr[i]; + // Class funcs + if (auto metaclass = decl->objc.metaclass) { + auto metamethods = metaclass->objc.methodList; + + for(size_t i = 0; i < metamethods.length; i++) { + auto method = metamethods.ptr[i]; // Static functions are class methods. if (method->isStatic()) { @@ -404,6 +365,22 @@ void ObjcClasslike::onScan() { } } } + + auto methods = decl->objc.methodList; + for(size_t i = 0; i < methods.length; i++) { + auto method = methods.ptr[i]; + + // Static functions are class methods. + if (method->isStatic()) { + classMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } else { + instanceMethods.push_back( + new ObjcMethod(module, objc, method) + ); + } + } } LLConstant *ObjcClasslike::emitProtocolList() { @@ -758,6 +735,7 @@ LLConstant *ObjcProtocol::emitTable() { // Base Protocols if (auto baseProtocols = this->emitProtocolList()) { auto sym = getObjcProtoListSymbol(getName(), true); + protocolList = getOrCreateWeak(sym, baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); } @@ -767,33 +745,37 @@ LLConstant *ObjcProtocol::emitTable() { auto sym = getObjcProtoMethodListSymbol(getName(), true, false); classMethodList = makeGlobal(sym, classMethodConsts->getType(), OBJC_SECNAME_CONST); classMethodList->setInitializer(classMethodConsts); + classMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); classMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } - // Optional class methods - if (auto optClassMethodConsts = this->emitMethodList(classMethods, true)) { - auto sym = getObjcProtoMethodListSymbol(getName(), true, true); - optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST); - optClassMethodList->setInitializer(optClassMethodConsts); - optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); - optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); - } - // Instance methods if (auto instanceMethodConsts = this->emitMethodList(instanceMethods)) { auto sym = getObjcProtoMethodListSymbol(getName(), false, false); instanceMethodList = makeGlobal(sym, instanceMethodConsts->getType(), OBJC_SECNAME_CONST); instanceMethodList->setInitializer(instanceMethodConsts); + instanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); instanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } + // Optional class methods + if (auto optClassMethodConsts = this->emitMethodList(classMethods, true)) { + auto sym = getObjcProtoMethodListSymbol(getName(), true, true); + optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST); + optClassMethodList->setInitializer(optClassMethodConsts); + + optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); + } + // Optional instance methods if (auto optInstanceMethodConsts = this->emitMethodList(instanceMethods, true)) { auto sym = getObjcProtoMethodListSymbol(getName(), false, true); optInstanceMethodList = makeGlobal(sym, optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); optInstanceMethodList->setInitializer(optInstanceMethodConsts); + optInstanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); optInstanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } diff --git a/gen/objcgen.h b/gen/objcgen.h index 877f406376c..c24a5fb6515 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -200,6 +200,7 @@ class ObjcMethod : public ObjcObject { LLGlobalVariable *selref; LLGlobalVariable *name; LLGlobalVariable *type; + LLConstant *llfunc; }; // ivar_t From ccec2ea64c3978d92975b907cf12feb4cd0b65f9 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 13:05:12 +0100 Subject: [PATCH 53/67] Make ObjcMethod anon again --- gen/objcgen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 9e51ce12614..4220ef9c7f0 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -280,9 +280,9 @@ LLConstant *ObjcMethod::info(bool emitExtern) { if (!emitExtern && !decl->fbody) return nullptr; - return LLConstantStruct::get( - ObjcMethod::getObjcMethodType(module), - { name, type, llfunc } + return LLConstantStruct::getAnon( + { name, type, llfunc }, + true ); } From c8e743e2963fae1943ae6611acc2785af0ba1a48 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 14:05:29 +0100 Subject: [PATCH 54/67] Fix missing emit calls --- gen/objcgen.cpp | 13 ------------- gen/objcgen.h | 29 ++++++++--------------------- gen/tocall.cpp | 4 +--- 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 4220ef9c7f0..086598fccc8 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -274,9 +274,6 @@ LLConstant *ObjcMethod::emit() { // Implements the objc_method structure LLConstant *ObjcMethod::info(bool emitExtern) { - if (!name) - emit(); - if (!emitExtern && !decl->fbody) return nullptr; @@ -287,10 +284,6 @@ LLConstant *ObjcMethod::info(bool emitExtern) { } LLConstant *ObjcMethod::get() { - isUsed = true; - if (!name) - emit(); - return selref; } @@ -326,7 +319,6 @@ LLConstant *ObjcIvar::emit() { // Implements the objc_method structure LLConstant *ObjcIvar::info() { LLConstantList members; - this->get(); members.push_back(offset); members.push_back(name); @@ -710,11 +702,6 @@ LLConstant *ObjcClass::ref() { } LLConstant *ObjcClass::get() { - isUsed = true; - - if (!classTable) - return emit(); - return classTable; } diff --git a/gen/objcgen.h b/gen/objcgen.h index c24a5fb6515..1a45ef981b3 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -94,10 +94,9 @@ std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); // LLVM module. class ObjcObject { public: - ObjcObject(llvm::Module &module, ObjCState &objc) : module(module), objc(objc) { } - - // Whether the object is used. - bool isUsed; + ObjcObject(llvm::Module &module, ObjCState &objc) : module(module), objc(objc) { + this->emit(); + } // Gets a reference to the object in the module. virtual LLConstant *get() { return nullptr; } @@ -154,7 +153,7 @@ class ObjcMethod : public ObjcObject { FuncDeclaration *decl; ObjcMethod(llvm::Module &module, ObjCState &objc, FuncDeclaration *decl) : - ObjcObject(module, objc), decl(decl) { } + ObjcObject(module, objc), decl(decl) { this->emit(); } // Gets the main reference to the object. LLConstant *get() override; @@ -209,7 +208,7 @@ class ObjcIvar : public ObjcObject { VarDeclaration *decl; ObjcIvar(llvm::Module &module, ObjCState &objc, VarDeclaration *decl) : - ObjcObject(module, objc), decl(decl) { } + ObjcObject(module, objc), decl(decl) { this->emit(); } // Gets the type for an Objective-C ivar_t struct. static LLStructType *getObjcIvarType(const llvm::Module& module) { @@ -232,19 +231,11 @@ class ObjcIvar : public ObjcObject { } LLConstant *getOffset() { - isUsed = true; - if (!name) - emit(); - return offset; } // Gets the main reference to the object. LLConstant *get() override { - isUsed = true; - if (!name) - emit(); - return name; } @@ -269,7 +260,7 @@ class ObjcClasslike : public ObjcObject { ClassDeclaration *decl; ObjcClasslike(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcObject(module, objc), decl(decl) { } + ObjcObject(module, objc), decl(decl) { this->emit(); } const char *getName() override; @@ -322,7 +313,7 @@ class ObjcClasslike : public ObjcObject { class ObjcProtocol : public ObjcClasslike { public: ObjcProtocol(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcClasslike(module, objc, decl) { } + ObjcClasslike(module, objc, decl) { this->emit(); } // Gets the type of an Objective-C class_t struct static LLStructType *getObjcProtocolType(const llvm::Module& module) { @@ -359,10 +350,6 @@ class ObjcProtocol : public ObjcClasslike { // Gets the main reference to the object. LLConstant *get() override { - isUsed = true; - if (!protocolTable) - emit(); - return protocolTable; } @@ -389,7 +376,7 @@ class ObjcProtocol : public ObjcClasslike { class ObjcClass : public ObjcClasslike { public: ObjcClass(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcClasslike(module, objc, decl) { } + ObjcClasslike(module, objc, decl) { this->emit(); } // Gets objective-c the flags for the class declaration static size_t getClassFlags(const ClassDeclaration& decl); diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 0e9a2eafe78..ac9723dcc45 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -758,7 +758,7 @@ class ImplicitArgumentsBuilder { } else { // ... or a normal 'this' argument - args.push_back(dfnval->vthis); + args.push_back(DtoBitCast(dfnval->vthis, argtype)); } } else if (isDelegateCall) { // ... or a delegate context arg @@ -795,8 +795,6 @@ class ImplicitArgumentsBuilder { assert(dfnval); auto method = gIR->objc.getMethodRef(dfnval->func); - assert(method); - auto methodptr = method->get(); args.push_back(DtoLoad(methodptr->getType(), methodptr)); } From f9bf25a62e564e1bf7e7347689952b6da9113883 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Tue, 26 Nov 2024 15:35:46 +0100 Subject: [PATCH 55/67] Fix classref gen --- gen/objcgen.cpp | 16 ++++++---------- gen/objcgen.h | 10 ++++++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 086598fccc8..c58791e043d 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -132,7 +132,7 @@ std::string getObjcClassSymbol(const char *name, bool meta) { } std::string getObjcClassLabelSymbol(const char *name) { - return getObjcSymbolName("_OBJC_LABEL_CLASS_$_", name); + return getObjcSymbolName("OBJC_LABEL_CLASS_$_", name); } std::string getObjcClassMethodListSymbol(const char *className, bool meta) { @@ -636,7 +636,6 @@ LLConstant *ObjcClass::emit() { // Extern classes only need non-ro refs. if (decl->objc.isExtern) { - this->scan(); classTable = makeGlobal(className, ObjcClass::getObjcClassType(module), "", true, false); metaClassTable = makeGlobal(metaName, ObjcClass::getObjcClassType(module), "", true, false); @@ -649,8 +648,10 @@ LLConstant *ObjcClass::emit() { } // Still emit classref - classref = getOrCreateWeak(getObjcClassLabelSymbol(name), getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); - classref->setInitializer(classTable); + classref = makeGlobalRef(classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS, false, true); + this->retain(classref); + this->retain(metaClassTable); + this->retain(classTable); return classTable; } @@ -664,10 +665,7 @@ LLConstant *ObjcClass::emit() { classRoTable = getOrCreate(classNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); metaClassRoTable = getOrCreate(metaNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); - - classref = getOrCreateWeak(getObjcClassLabelSymbol(name), getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); - classref->setInitializer(classTable); - + classref = makeGlobalRef(classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS, false, true); this->scan(); // Emit their structure. @@ -862,7 +860,6 @@ ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { if (auto id = cd->isInterfaceDeclaration()) { if (auto proto = getProtocolRef(id)) { - proto->scan(); // Attempt to get the method, if not found // try the parent. @@ -878,7 +875,6 @@ ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { return method; } } else if (auto klass = getClassRef(cd)) { - klass->scan(); // Attempt to get the method, if not found // try the parent. diff --git a/gen/objcgen.h b/gen/objcgen.h index 1a45ef981b3..c5e8a87ecab 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -313,7 +313,10 @@ class ObjcClasslike : public ObjcObject { class ObjcProtocol : public ObjcClasslike { public: ObjcProtocol(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcClasslike(module, objc, decl) { this->emit(); } + ObjcClasslike(module, objc, decl) { + this->emit(); + this->scan(); + } // Gets the type of an Objective-C class_t struct static LLStructType *getObjcProtocolType(const llvm::Module& module) { @@ -376,7 +379,10 @@ class ObjcProtocol : public ObjcClasslike { class ObjcClass : public ObjcClasslike { public: ObjcClass(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcClasslike(module, objc, decl) { this->emit(); } + ObjcClasslike(module, objc, decl) { + this->emit(); + this->scan(); + } // Gets objective-c the flags for the class declaration static size_t getClassFlags(const ClassDeclaration& decl); From f577f51689e07d1191308a78cecd382554f9aeae Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 04:34:10 +0100 Subject: [PATCH 56/67] Implement some of the requested changes --- dmd/frontend.h | 2 -- dmd/objc.d | 50 ++++++++++++++++++++++++++----------------------- dmd/objc.h | 5 +++-- gen/abi/abi.h | 2 +- tests/dmd/run.d | 5 ++++- 5 files changed, 35 insertions(+), 29 deletions(-) diff --git a/dmd/frontend.h b/dmd/frontend.h index 6faa84e6cc4..46b0efd8d16 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -7454,7 +7454,6 @@ class Objc virtual void checkLinkage(FuncDeclaration* fd) = 0; virtual bool isVirtual(const FuncDeclaration* const fd) const = 0; virtual void setAsOptional(FuncDeclaration* functionDeclaration, Scope* sc) const = 0; - virtual void setAsSwiftStub(FuncDeclaration* functionDeclaration, Scope* sc) const = 0; virtual void validateOptional(FuncDeclaration* functionDeclaration) const = 0; virtual ClassDeclaration* getParent(FuncDeclaration* fd, ClassDeclaration* cd) const = 0; virtual void addToClassMethodList(FuncDeclaration* fd, ClassDeclaration* cd) const = 0; @@ -8739,7 +8738,6 @@ struct Id final static Identifier* udaGNUAbiTag; static Identifier* udaSelector; static Identifier* udaOptional; - static Identifier* udaSwiftStub; static Identifier* udaMustUse; static Identifier* udaStandalone; static Identifier* TRUE; diff --git a/dmd/objc.d b/dmd/objc.d index e1da1ccafc1..fcc80029850 100644 --- a/dmd/objc.d +++ b/dmd/objc.d @@ -274,6 +274,7 @@ extern(C++) abstract class Objc * cd = the class declaration to set as a swift stub * sc = the scope from the semantic phase */ + version(IN_LLVM) abstract void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const; /** @@ -464,6 +465,7 @@ static if (!IN_LLVM) // noop } + version(IN_LLVM) override void setAsSwiftStub(ClassDeclaration, Scope*) const { // noop @@ -845,38 +847,40 @@ version (IN_LLVM) {} else "of Objective-C classes. Please use the Objective-C runtime instead"); } - override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const - { - const count = declaredAsSwiftStubCount(cd, sc); - cd.objc.isSwiftStub = count > 0; + version(IN_LLVM) { + override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const + { + const count = declaredAsSwiftStubCount(cd, sc); + cd.objc.isSwiftStub = count > 0; - if (count > 1) - .error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars); - } + if (count > 1) + .error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars); + } - /// Returns: the number of times `cd` has been declared as optional. - private int declaredAsSwiftStubCount(ClassDeclaration cd, Scope* sc) const - { - int count; + /// Returns: the number of times `cd` has been declared as optional. + private int declaredAsSwiftStubCount(ClassDeclaration cd, Scope* sc) const + { + int count; - foreachUda(cd, sc, (e) { - if (!e.isTypeExp()) - return 0; + foreachUda(cd, sc, (e) { + if (!e.isTypeExp()) + return 0; - auto typeExp = e.isTypeExp(); + auto typeExp = e.isTypeExp(); - if (typeExp.type.ty != Tenum) - return 0; + if (typeExp.type.ty != Tenum) + return 0; - auto typeEnum = cast(TypeEnum) typeExp.type; + auto typeEnum = cast(TypeEnum) typeExp.type; - if (isCoreUda(typeEnum.sym, Id.udaSwiftStub)) - count++; + if (isCoreUda(typeEnum.sym, Id.udaSwiftStub)) + count++; - return 0; - }); + return 0; + }); - return count; + return count; + } } } diff --git a/dmd/objc.h b/dmd/objc.h index ed16e8bdda6..7ad1a174ee4 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -68,14 +68,15 @@ class Objc virtual void checkLinkage(FuncDeclaration* fd) = 0; virtual bool isVirtual(const FuncDeclaration*) const = 0; virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0; +#if IN_LLVM + virtual void setAsSwiftStub(ClassDeclaration* cd, Scope *sc) const = 0; +#endif virtual void validateOptional(FuncDeclaration *fd) const = 0; virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0; virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0; virtual AggregateDeclaration* isThis(FuncDeclaration* fd) = 0; virtual VarDeclaration* createSelectorParameter(FuncDeclaration*, Scope*) const = 0; - virtual void setAsSwiftStub(ClassDeclaration* cd, Scope *sc) const = 0; - virtual void setMetaclass(InterfaceDeclaration* id, Scope*) const = 0; virtual void setMetaclass(ClassDeclaration* id, Scope*) const = 0; virtual ClassDeclaration* getRuntimeMetaclass(ClassDeclaration* cd) = 0; diff --git a/gen/abi/abi.h b/gen/abi/abi.h index 02656ab4231..3545fb531af 100644 --- a/gen/abi/abi.h +++ b/gen/abi/abi.h @@ -119,7 +119,7 @@ struct TargetABI { } /// Returns true if the target is darwin-based. - virtual bool isDarwin() { + bool isDarwin() { return global.params.targetTriple->isOSDarwin(); } diff --git a/tests/dmd/run.d b/tests/dmd/run.d index 2d9e43bb027..f2fc0be3f04 100755 --- a/tests/dmd/run.d +++ b/tests/dmd/run.d @@ -619,7 +619,10 @@ string[string] getEnvironment() } version(OSX) - env["D_OBJC"] = "1"; + version (IN_LLVM) + env["D_OBJC"] = "1"; + else version(X86_64) + env["D_OBJC"] = "1"; } return env; } From 0d84d40be80b3b8096df139e92788fa5eba5a358 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 04:51:47 +0100 Subject: [PATCH 57/67] Enable compilable tests --- tests/dmd/compilable/objc_class.d | 1 - tests/dmd/compilable/objc_interface_final_19654.d | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/dmd/compilable/objc_class.d b/tests/dmd/compilable/objc_class.d index 2b595d47d26..57d4b0d6cfd 100644 --- a/tests/dmd/compilable/objc_class.d +++ b/tests/dmd/compilable/objc_class.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: import core.attribute : selector; diff --git a/tests/dmd/compilable/objc_interface_final_19654.d b/tests/dmd/compilable/objc_interface_final_19654.d index fb8a4ff64c5..01be3219c60 100644 --- a/tests/dmd/compilable/objc_interface_final_19654.d +++ b/tests/dmd/compilable/objc_interface_final_19654.d @@ -1,4 +1,3 @@ -// DISABLED: LDC // EXTRA_OBJC_SOURCES: import core.attribute : selector; From 8ac9a367e6e9fb0765d3ddd65d4a90524661ad1d Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 05:26:04 +0100 Subject: [PATCH 58/67] Fix property selector gen, ugly hack for final funcs. --- gen/functions.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/gen/functions.cpp b/gen/functions.cpp index ad347937f16..788e9309d70 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -140,12 +140,24 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, } bool hasObjCSelector = false; + auto ftype = (TypeFunction*)fd->type; if (fd && fd->_linkage == LINK::objc) { if (fd->objc.selector) { hasObjCSelector = true; } else if (fd->parent->isClassDeclaration()) { - error(fd->loc, "%s `%s` is missing Objective-C `@selector`", fd->kind(), - fd->toPrettyChars()); + if(fd->isFinal() || ftype->isproperty()) { + + // HACK: Ugly hack, but final functions for some reason don't actually declare a selector. + // However, this does make it more flexible. + // Also this will automatically generate selectors for @property declared + // functions, which the DIP specifies. + // Final function selector gen should be fixed, however. + fd->objc.selector = ObjcSelector::create(fd); + hasObjCSelector = true; + } else { + error(fd->loc, "%s `%s` is missing Objective-C `@selector`", fd->kind(), + fd->toPrettyChars()); + } } } if (hasObjCSelector) { From 5410e04773a289c044c776ad00cc592bca803dc7 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 05:35:16 +0100 Subject: [PATCH 59/67] Fix segfault in referencing fd->type --- gen/functions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gen/functions.cpp b/gen/functions.cpp index 788e9309d70..7fa5121cc30 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -140,8 +140,9 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, } bool hasObjCSelector = false; - auto ftype = (TypeFunction*)fd->type; if (fd && fd->_linkage == LINK::objc) { + auto ftype = (TypeFunction*)fd->type; + if (fd->objc.selector) { hasObjCSelector = true; } else if (fd->parent->isClassDeclaration()) { From 1fc06e49d9fdda5c6ca2d80cdff0447f54e45ab0 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 09:44:33 +0100 Subject: [PATCH 60/67] Refactor implementation --- gen/classes.cpp | 9 +- gen/declarations.cpp | 4 +- gen/llvmhelpers.cpp | 2 +- gen/objcgen.cpp | 1046 ++++++++++++++++++++---------------------- gen/objcgen.h | 515 ++++----------------- gen/tocall.cpp | 5 +- gen/toir.cpp | 14 +- gen/tollvm.cpp | 19 + gen/tollvm.h | 2 + 9 files changed, 608 insertions(+), 1008 deletions(-) diff --git a/gen/classes.cpp b/gen/classes.cpp index aaa1e6484a6..51a7a554f3e 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -363,11 +363,8 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { // Get class_t handle LLValue *objTy = getNullPtr(); - if (auto chndl = _to->isClassHandle()) { - if (auto ihndl = chndl->isInterfaceDeclaration()) - objTy = gIR->objc.getProtocolRef(ihndl)->deref(getOpaquePtrType()); - else - objTy = gIR->objc.getClassRef(chndl)->deref(getOpaquePtrType()); + if (auto thandle = _to->isClassHandle()) { + objTy = gIR->objc.deref(thandle, getOpaquePtrType()); } // objc_opt_isKindOfClass will check if id is null @@ -427,7 +424,7 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { // Get prototype_t handle LLValue *protoTy = getNullPtr(); if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) { - protoTy = gIR->objc.getProtocolRef(ifhndl)->deref(getOpaquePtrType()); + protoTy = gIR->objc.deref(ifhndl, getOpaquePtrType()); } // Class && kindOfProtocolFunc(Class) ? id : null diff --git a/gen/declarations.cpp b/gen/declarations.cpp index ed8902f70c3..9613d190bc7 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -111,7 +111,7 @@ class CodegenVisitor : public Visitor { // Objective-C protocols don't have TypeInfo. if (decl->classKind == ClassKind::objc) { - gIR->objc.emit(decl); + gIR->objc.getProtocol(decl); return; } @@ -213,7 +213,7 @@ class CodegenVisitor : public Visitor { // Objective-C class structure is initialized by calling getClassRef. if (decl->classKind == ClassKind::objc) { - gIR->objc.emit(decl); + gIR->objc.getClass(decl); return; } diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 767752f1ec1..d4818de8996 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1843,7 +1843,7 @@ DLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad, if (ad->classKind == ClassKind::objc) { auto tHandle = getI32Type(); - auto tOffset = DtoLoad(tHandle, gIR->objc.getIVarOffset(ad->isClassDeclaration(), vd)); + auto tOffset = DtoLoad(tHandle, gIR->objc.getIvar(vd)->offset); // Offset is now stored in tOffset. LLValue *ptr = src; diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index c58791e043d..5773819304a 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -35,7 +35,7 @@ bool objc_isSupported(const llvm::Triple &triple) { } // TYPE ENCODINGS -std::string getTypeEncoding(Type *t) { +std::string objcGetTypeEncoding(Type *t) { std::string tmp; switch (t->ty) { case TY::Tclass: { @@ -45,12 +45,12 @@ std::string getTypeEncoding(Type *t) { return "?"; } case TY::Tfunction: { - tmp = getTypeEncoding(t->nextOf()); + tmp = objcGetTypeEncoding(t->nextOf()); tmp.append("@:"); if (auto func = t->isTypeFunction()) { for (size_t i = 0; i < func->parameterList.length(); i++) - tmp.append(getTypeEncoding(func->parameterList[i]->type)); + tmp.append(objcGetTypeEncoding(func->parameterList[i]->type)); } return tmp; } @@ -61,7 +61,7 @@ std::string getTypeEncoding(Type *t) { return "*"; tmp.append("^"); - tmp.append(getTypeEncoding(t->nextOf())); + tmp.append(objcGetTypeEncoding(t->nextOf())); return tmp; } case TY::Tsarray: { @@ -72,7 +72,7 @@ std::string getTypeEncoding(Type *t) { uinteger_t count = typ->dim->toUInteger(); tmp.append("["); tmp.append(std::to_string(count)); - tmp.append(getTypeEncoding(typ->next)); + tmp.append(objcGetTypeEncoding(typ->next)); tmp.append("]"); return tmp; } @@ -90,7 +90,7 @@ std::string getTypeEncoding(Type *t) { tmp.append("="); for(unsigned int i = 0; i < sym->numArgTypes(); i++) { - tmp.append(getTypeEncoding(sym->argType(i))); + tmp.append(objcGetTypeEncoding(sym->argType(i))); } tmp.append(isUnion ? ")" : "}"); @@ -123,342 +123,248 @@ std::string getTypeEncoding(Type *t) { // STRING HELPERS // -std::string getObjcClassRoSymbol(const char *name, bool meta) { - return getObjcSymbolName(meta ? "_OBJC_METACLASS_RO_$_" : "_OBJC_CLASS_RO_$_", name); +std::string objcGetClassRoSymbol(const char *name, bool meta) { + return objcGetSymbolName(meta ? "_OBJC_METACLASS_RO_$_" : "_OBJC_CLASS_RO_$_", name); } -std::string getObjcClassSymbol(const char *name, bool meta) { - return getObjcSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", name); +std::string objcGetClassSymbol(const char *name, bool meta) { + return objcGetSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", name); } -std::string getObjcClassLabelSymbol(const char *name) { - return getObjcSymbolName("OBJC_LABEL_CLASS_$_", name); +std::string objcGetClassLabelSymbol(const char *name) { + return objcGetSymbolName("OBJC_LABEL_CLASS_$_", name); } -std::string getObjcClassMethodListSymbol(const char *className, bool meta) { - return getObjcSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className); +std::string objcGetClassMethodListSymbol(const char *className, bool meta) { + return objcGetSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className); } -std::string getObjcProtoMethodListSymbol(const char *className, bool meta, bool optional) { +std::string objcGetProtoMethodListSymbol(const char *className, bool meta, bool optional) { return optional ? - getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_", className) : - getObjcSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className); + objcGetSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_", className) : + objcGetSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className); } -std::string getObjcIvarListSymbol(const char *className) { - return getObjcSymbolName("_OBJC_$_INSTANCE_VARIABLES_", className); +std::string objcGetIvarListSymbol(const char *className) { + return objcGetSymbolName("_OBJC_$_INSTANCE_VARIABLES_", className); } -std::string getObjcProtoSymbol(const char *name) { - return getObjcSymbolName("_OBJC_PROTOCOL_$_", name); +std::string objcGetProtoSymbol(const char *name) { + return objcGetSymbolName("_OBJC_PROTOCOL_$_", name); } -std::string getObjcProtoListSymbol(const char *name, bool isProtocol) { - return getObjcSymbolName(isProtocol ? "_OBJC_$_PROTOCOL_REFS_" : "_OBJC_CLASS_PROTOCOLS_$_", name); +std::string objcGetProtoListSymbol(const char *name, bool isProtocol) { + return objcGetSymbolName(isProtocol ? "_OBJC_$_PROTOCOL_REFS_" : "_OBJC_CLASS_PROTOCOLS_$_", name); } -std::string getObjcProtoLabelSymbol(const char *name) { - return getObjcSymbolName("_OBJC_LABEL_PROTOCOL_$_", name); +std::string objcGetProtoLabelSymbol(const char *name) { + return objcGetSymbolName("_OBJC_LABEL_PROTOCOL_$_", name); } -std::string getObjcIvarSymbol(const char *className, const char *varName) { +std::string objcGetIvarSymbol(const char *className, const char *varName) { return ("OBJC_IVAR_$_" + std::string(className) + "." + std::string(varName)); } -std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName) { +std::string objcGetSymbolName(const char *dsymPrefix, const char *dsymName) { return (std::string(dsymPrefix) + std::string(dsymName)); } -const char *getResolvedName(ClassDeclaration *decl) { - if (auto mo = decl->pMangleOverride) { - return mo->id->toChars(); - } - - return decl->objc.identifier->toChars(); -} +const char *objcResolveName(Dsymbol *decl) { -// -// ObjcObject -// -void ObjcObject::retain(LLGlobalVariable *toRetain) { - for(auto elem : objc.retained) { - if (elem == toRetain) - return; + // Function names are based on selector. + if (auto funcdecl = decl->isFuncDeclaration()) { + return funcdecl->objc.selector->stringvalue; } - objc.retained.push_back(toRetain); -} + // Class and interface names are determined by objc identifier. + if (auto classdecl = decl->isClassDeclaration()) { + return classdecl->objc.identifier->toChars(); + } -LLConstant *offsetIvar(size_t ivaroffset) { - return DtoConstUint(getPointerSize()+ivaroffset); + return decl->ident->toChars(); } -LLConstant *ObjcObject::emitList(llvm::Module &module, LLConstantList objects, bool alignSizeT) { - LLConstantList members; - - // Emit nullptr for empty lists. - if (objects.empty()) - return nullptr; - - // Size of stored struct. - size_t allocSize = getTypeAllocSize(objects.front()->getType()); - members.push_back( - alignSizeT ? - DtoConstSize_t(allocSize) : - DtoConstUint(allocSize) - ); - - // Method count - members.push_back(DtoConstUint( - objects.size() - )); +// +// TYPE HELPERS +// - // Insert all the objects in the constant list. - members.insert( - members.end(), - objects.begin(), - objects.end() +LLStructType *objcGetStubClassType(const llvm::Module& module) { + auto stubClassType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_STUBCLASS); + if (stubClassType) + return stubClassType; + + stubClassType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // function pointer. + }, + OBJC_STRUCTNAME_STUBCLASS ); - - return LLConstantStruct::getAnon( - members, - true + return stubClassType; +} + +LLStructType *objcGetClassRoType(const llvm::Module& module) { + auto classRoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASSRO); + if (classRoType) + return classRoType; + + classRoType = LLStructType::create( + module.getContext(), + { + getI32Type(), // uint32_t flags + getI32Type(), // uint32_t instanceStart + getI32Type(), // uint32_t instanceSize + getOpaquePtrType(), // void* layoutOrNonMetaClass + getOpaquePtrType(), // const char* name + getOpaquePtrType(), // method_list_t* baseMethods + getOpaquePtrType(), // protocol_list_t* baseProtocols + getOpaquePtrType(), // ivar_list_t* ivars + getOpaquePtrType(), // const uint8_t* weakIvarLayout + getOpaquePtrType(), // property_list_t* baseProperties + }, + OBJC_STRUCTNAME_CLASSRO ); -} - -LLConstant *ObjcObject::emitCountList(llvm::Module &module, LLConstantList objects, bool alignSizeT) { - LLConstantList members; - - // Method count - members.push_back( - alignSizeT ? - DtoConstSize_t(objects.size()) : - DtoConstUint(objects.size()) + return classRoType; +} + +LLStructType *objcGetClassType(const llvm::Module& module) { + auto classType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASS); + if (classType) + return classType; + + classType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // objc_object* superclass + getOpaquePtrType(), // cache_t* cache + getOpaquePtrType(), // void* vtbl; (unused, set to null) + getOpaquePtrType(), // class_ro_t* ro + }, + OBJC_STRUCTNAME_CLASS ); - - // Insert all the objects in the constant list. - if (!objects.empty()) { - members.insert( - members.end(), - objects.begin(), - objects.end() - ); - } - - // These lists need to be null terminated. - members.push_back(getNullPtr()); - return LLConstantStruct::getAnon( - members, - true + return classType; +} + +LLStructType *objcGetMethodType(const llvm::Module& module) { + auto methType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_METHOD); + if (methType) + return methType; + + return LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // SEL name + getOpaquePtrType(), // const char *types + getOpaquePtrType(), // IMP imp + }, + OBJC_STRUCTNAME_METHOD ); } - -// -// METHODS -// - -LLConstant *ObjcMethod::emit() { - name = makeGlobalStr(getSelector(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); - type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); - selref = makeGlobalRef(name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); - llfunc = decl->fbody ? - DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : - getNullPtr(); - - this->retain(name); - this->retain(type); - this->retain(selref); - return selref; -} - -// Implements the objc_method structure -LLConstant *ObjcMethod::info(bool emitExtern) { - if (!emitExtern && !decl->fbody) - return nullptr; - - return LLConstantStruct::getAnon( - { name, type, llfunc }, - true +LLStructType *objcGetIvarType(const llvm::Module& module) { + auto ivarType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_IVAR); + if (ivarType) + return ivarType; + + ivarType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // int32_t *offset + getOpaquePtrType(), // const char *name + getOpaquePtrType(), // const char *type + getI32Type(), // uint32_t alignment_raw + getI32Type(), // uint32_t size + }, + OBJC_STRUCTNAME_IVAR ); -} - -LLConstant *ObjcMethod::get() { - return selref; -} - -// -// INSTANCE VARIABLES -// - -LLConstant *ObjcIvar::emit() { - auto ivarsym = getObjcIvarSymbol(decl->parent->ident->toChars(), decl->ident->toChars()); - - // Extern, emit data. - if (auto klass = decl->parent->isClassDeclaration()) { - if (klass->objc.isExtern) { - name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true); - type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true); - - // It will be filled out by the runtime, but make sure it's there nontheless. - offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - offset->setInitializer(offsetIvar(0)); - return nullptr; - } - - // Non-extern, emit data. - name = makeGlobalStr(decl->ident->toChars(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); - type = makeGlobalStr(getTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); - - offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - offset->setInitializer(offsetIvar(decl->offset)); - } - return nullptr; -} - -// Implements the objc_method structure -LLConstant *ObjcIvar::info() { - LLConstantList members; - - members.push_back(offset); - members.push_back(name); - members.push_back(type); - members.push_back(DtoConstUint(decl->alignment.isDefault() ? -1 : decl->alignment.get())); - members.push_back(DtoConstUint(decl->size(decl->loc))); - - return LLConstantStruct::get( - ObjcIvar::getObjcIvarType(module), - members + return ivarType; +} + +LLStructType *objcGetProtocolType(const llvm::Module& module) { + auto protoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_PROTO); + if (protoType) + return protoType; + + protoType = LLStructType::create( + module.getContext(), + { + getOpaquePtrType(), // objc_object* isa + getOpaquePtrType(), // protocol_list_t* protocols + getOpaquePtrType(), // const char *mangledName + getOpaquePtrType(), // method_list_t* instanceMethods + getOpaquePtrType(), // method_list_t* classMethods + getOpaquePtrType(), // method_list_t* optionalInstanceMethods + getOpaquePtrType(), // method_list_t* optionalClassMethods + getOpaquePtrType(), // property_list_t* instanceProperties + getI32Type(), // uint32_t size + getI32Type(), // uint32_t flags + + // Further fields follow but are optional and are fixed up at + // runtime. + }, + OBJC_STRUCTNAME_PROTO ); + return protoType; } // -// CLASS-LIKES +// *_list_t helpers. // -void ObjcClasslike::onScan() { - - // Class funcs - if (auto metaclass = decl->objc.metaclass) { - auto metamethods = metaclass->objc.methodList; - - for(size_t i = 0; i < metamethods.length; i++) { - auto method = metamethods.ptr[i]; - - // Static functions are class methods. - if (method->isStatic()) { - classMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } else { - instanceMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } - } - } - - auto methods = decl->objc.methodList; - for(size_t i = 0; i < methods.length; i++) { - auto method = methods.ptr[i]; - - // Static functions are class methods. - if (method->isStatic()) { - classMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } else { - instanceMethods.push_back( - new ObjcMethod(module, objc, method) - ); - } - } -} - -LLConstant *ObjcClasslike::emitProtocolList() { - LLConstantList list; - auto ifaces = decl->interfaces; - - // Protocols - for(size_t i = 0; i < ifaces.length; i++) { - if (auto iface = ifaces.ptr[i]) { - - // Only add interfaces which have objective-c linkage - // TODO: throw an error if you try to include a non-objective-c interface? - if (auto ifacesym = (InterfaceDeclaration *)iface->sym) { - if (ifacesym->classKind == ClassKind::objc) { - if (auto proto = this->objc.getProtocolRef(ifacesym)) { - list.push_back(proto->get()); - } - } - } - } - } - - return ObjcObject::emitCountList( - module, - list, - true - ); -} - -LLConstant *ObjcClasslike::emitMethodList(ObjcList &methods, bool optionalMethods) { - LLConstantList toAdd; - +LLConstant *objcEmitList(llvm::Module &module, LLConstantList objects, bool alignSizeT, bool countOnly) { + LLConstantList members; + // Emit nullptr for empty lists. - if (methods.empty()) + if (objects.empty()) return nullptr; - bool isProtocol = decl->isInterfaceDeclaration(); - - // Find out how many functions have actual bodies. - for(size_t i = 0; i < methods.size(); i++) { - auto method = methods[i]; - - if (method->isOptional() == optionalMethods) { - auto methodInfo = method->info(isProtocol); + if (!countOnly) { - if (methodInfo) - toAdd.push_back(methodInfo); - } + // Size of stored struct. + size_t allocSize = getTypeAllocSize(objects.front()->getType()); + members.push_back( + alignSizeT ? + DtoConstSize_t(allocSize) : + DtoConstUint(allocSize) + ); } - // List is empty too, but due to optionals not matching. - if (toAdd.empty()) - return nullptr; - - return ObjcObject::emitList( - module, - toAdd + // Object count + size_t objCount = objects.size(); + members.push_back( + alignSizeT ? + DtoConstSize_t(objCount) : + DtoConstUint(objCount) ); -} -LLGlobalVariable *ObjcClasslike::emitName() { - if (className) - return className; - - className = makeGlobalStr(getName(), "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); - return className; -} + // Insert all the objects in to a constant array. + // This matches the codegen by the objective-c compiler. + auto arrayType = LLArrayType::get( + objects.front()->getType(), + objects.size() + ); + members.push_back(LLConstantArray::get( + arrayType, + objects + )); -const char *ObjcClasslike::getName() { - return getResolvedName(decl); + return LLConstantStruct::getAnon( + members, + true + ); } // -// CLASSES +// Other helpers // -LLGlobalVariable *ObjcClass::getIVarOffset(VarDeclaration *vd) { - auto ivarsym = getObjcIvarSymbol(decl->ident->toChars(), vd->ident->toChars()); - auto ivoffset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - ivoffset->setInitializer(offsetIvar(vd->offset)); - this->retain(ivoffset); - - return ivoffset; +LLConstant *objcOffsetIvar(size_t ivaroffset) { + return DtoConstUint(getPointerSize()+ivaroffset); } -size_t ObjcClass::getClassFlags(const ClassDeclaration& decl) { +size_t objcGetClassFlags(const ClassDeclaration& decl) { size_t flags = 0; if (!decl.baseClass) flags |= RO_ROOT; @@ -469,37 +375,27 @@ size_t ObjcClass::getClassFlags(const ClassDeclaration& decl) { return flags; } -void ObjcClass::onScan() { - ObjcClasslike::onScan(); +ClassDeclaration *objcGetRootMetaClass(ClassDeclaration *decl) { + auto curr = decl; + while (curr->baseClass) + curr = curr->baseClass; - // Push on all the ivars. - for(size_t i = 0; i < decl->fields.size(); i++) { - auto ivar = new ObjcIvar(module, objc, decl->fields[i]); - ivars.push_back(ivar); - ivar->get(); - } + return curr; } -LLConstant *ObjcClass::emitIvarList() { - LLConstantList ivarList; - - // Push on all the ivars. - for(size_t i = 0; i < ivars.size(); i++) { - auto ivarInfo = ivars[i]->info(); - - if (ivarInfo) - ivarList.push_back(ivarInfo); - } - - return this->emitList( - module, - ivarList - ); +ClassDeclaration *objcGetSuper(ClassDeclaration *decl, bool meta) { + return (decl->objc.isRootClass() || !decl->baseClass) ? + decl : + decl->baseClass; } -ptrdiff_t ObjcClass::getInstanceStart(bool meta) { +// +// CLASSES +// + +ptrdiff_t objcGetInstanceStart(llvm::Module &module, ClassDeclaration *decl, bool meta) { ptrdiff_t start = meta ? - getTypeAllocSize(ObjcClass::getObjcClassType(module)) : + getTypeAllocSize(objcGetClassType(module)) : getPointerSize(); // Meta-classes have no body. @@ -516,9 +412,9 @@ ptrdiff_t ObjcClass::getInstanceStart(bool meta) { return start; } -size_t ObjcClass::getInstanceSize(bool meta) { +size_t objcGetInstanceSize(llvm::Module &module, ClassDeclaration *decl, bool meta) { size_t start = meta ? - getTypeAllocSize(ObjcClass::getObjcClassType(module)) : + getTypeAllocSize(objcGetClassType(module)) : getPointerSize(); if (meta) @@ -527,187 +423,159 @@ size_t ObjcClass::getInstanceSize(bool meta) { return start+decl->size(decl->loc); } -LLConstant *ObjcClass::getRootMetaClass() { - auto curr = decl; - while (curr->baseClass) - curr = curr->baseClass; - - auto name = getObjcClassSymbol(curr->objc.identifier->toChars(), true); - return getOrCreate( - name, - ObjcClass::getObjcClassType(module), - curr->objc.isExtern ? "" : OBJC_SECNAME_DATA - ); +// Gets the empty cache variable, and creates a reference to it +// if needed. +LLGlobalVariable *getEmptyCache() { + static LLGlobalVariable *objcCache; + if(!objcCache) + objcCache = makeGlobal("_objc_empty_cache", nullptr, "", true, false); + return objcCache; } -LLConstant *ObjcClass::getSuper(bool meta) { - if (decl->objc.isRootClass() || !decl->baseClass) { - return meta ? classTable : metaClassTable; +LLGlobalVariable *ObjCState::getClassRoTable(ClassDeclaration *decl) { + if (classRoTables.find(decl) != classRoTables.end()) { + return classRoTables[decl]; } + + // No need to generate RO tables for externs. + if (decl->objc.isExtern) + return nullptr; - auto super = decl->baseClass; - auto superName = getObjcClassSymbol(super->objc.identifier->toChars(), meta); - return getOrCreate( - superName, - ObjcClass::getObjcClassType(module), - super->objc.isExtern ? "" : OBJC_SECNAME_DATA - ); -} - -void ObjcClass::emitTable(LLGlobalVariable *table, LLConstant *meta, LLConstant *super, LLConstant *roTable) { - LLConstantList members; - members.push_back(meta); - members.push_back(super); - members.push_back(getEmptyCache()); - members.push_back(getNullPtr()); - members.push_back(roTable); - - table->setInitializer(LLConstantStruct::get( - getObjcClassType(module), - members - )); -} + // Base Methods + auto meta = decl->objc.isMeta; + auto name = objcResolveName(decl); + auto sym = objcGetClassRoSymbol(name, meta); -void ObjcClass::emitRoTable(LLGlobalVariable *table, bool meta) { LLConstantList members; LLGlobalVariable *ivarList = nullptr; LLGlobalVariable *protocolList = nullptr; LLGlobalVariable *methodList = nullptr; - // Base Methods - auto baseMethods = meta ? - this->emitMethodList(classMethods) : - this->emitMethodList(instanceMethods); - - if (baseMethods) { - methodList = getOrCreate(getObjcClassMethodListSymbol(getName(), meta), baseMethods->getType(), OBJC_SECNAME_CONST); + // NOTE: createMethodList does not need to automatically select the meta-class + // in this instance. + if (auto baseMethods = createMethodList(decl, false, false)) { + methodList = getOrCreate(objcGetClassMethodListSymbol(name, meta), baseMethods->getType(), OBJC_SECNAME_CONST); methodList->setInitializer(baseMethods); } // Base Protocols - if (auto baseProtocols = emitProtocolList()) { - protocolList = getOrCreate(getObjcProtoListSymbol(getName(), false), baseProtocols->getType(), OBJC_SECNAME_CONST); + if (auto baseProtocols = createProtocolList(decl)) { + protocolList = getOrCreate(objcGetProtoListSymbol(name, false), baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); } if (!meta) { // Instance variables - if (auto baseIvars = emitIvarList()) { - ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); + if (auto baseIvars = createIvarList(decl)) { + ivarList = getOrCreate(objcGetIvarListSymbol(name), baseIvars->getType(), OBJC_SECNAME_CONST); ivarList->setInitializer(baseIvars); } } - // Build struct. - members.push_back(DtoConstUint(getClassFlags(meta ? *decl->objc.metaclass : *decl))); - members.push_back(DtoConstUint(getInstanceStart(meta))); - members.push_back(DtoConstUint(getInstanceSize(meta))); + members.push_back(DtoConstUint(objcGetClassFlags(meta ? *decl->objc.metaclass : *decl))); + members.push_back(DtoConstUint(objcGetInstanceStart(module, decl, meta))); + members.push_back(DtoConstUint(objcGetInstanceSize(module, decl, meta))); members.push_back(getNullPtr()); - members.push_back(this->emitName()); + members.push_back(getClass(decl)->name); members.push_back(wrapNull(methodList)); members.push_back(wrapNull(protocolList)); members.push_back(wrapNull(ivarList)); members.push_back(getNullPtr()); members.push_back(getNullPtr()); - table->setInitializer(LLConstantStruct::get( - getObjcClassRoType(module), - members - )); + auto table = makeGlobalWithBytes(sym, members, objcGetClassRoType(module)); + table->setSection(OBJC_SECNAME_DATA); + + classRoTables[decl] = table; + this->retain(table); + return table; } -LLConstant *ObjcClass::emit() { - if (decl->objc.isSwiftStub && !decl->objc.isExtern) { - error(decl->loc, "Cannot define non-extern swift stub classes!"); - fatal(); +LLGlobalVariable *ObjCState::getClassTable(ClassDeclaration *decl) { + if (classTables.find(decl) != classTables.end()) { + return classTables[decl]; } - assert(decl && !decl->isInterfaceDeclaration()); + auto meta = decl->objc.isMeta; + auto name = objcResolveName(decl); + auto sym = objcGetClassSymbol(name, meta); + + // Extern tables don't need a body. + if (decl->objc.isExtern) { + auto table = getOrCreateWeak(sym, objcGetClassType(module), "", true); - // Class already exists, just return that. - if (classTable) - return classTable; + classTables[decl] = table; + this->retain(table); + return table; + } - auto name = getName(); - auto className = getObjcClassSymbol(name, false); - auto metaName = getObjcClassSymbol(name, true); + LLConstantList members; + members.push_back(getClassTable(meta ? decl : decl->objc.metaclass)); // isa + members.push_back(getClassTable(objcGetSuper(decl, meta))); // super + members.push_back(getEmptyCache()); // cache + members.push_back(getNullPtr()); // vtbl + members.push_back(getClassRoTable(decl)); // ro - // Extern classes only need non-ro refs. - if (decl->objc.isExtern) { + auto table = makeGlobalWithBytes(sym, members, objcGetClassType(module)); + table->setSection(OBJC_SECNAME_DATA); - classTable = makeGlobal(className, ObjcClass::getObjcClassType(module), "", true, false); - metaClassTable = makeGlobal(metaName, ObjcClass::getObjcClassType(module), "", true, false); - - // Still emit ivars. - if (auto baseIvars = emitIvarList()) { - auto ivarList = getOrCreate(getObjcIvarListSymbol(getName()), baseIvars->getType(), OBJC_SECNAME_CONST); - ivarList->setInitializer(baseIvars); - this->retain(ivarList); - } + classTables[decl] = table; + this->retain(table); + return table; +} - // Still emit classref - classref = makeGlobalRef(classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS, false, true); - this->retain(classref); - this->retain(metaClassTable); - this->retain(classTable); - return classTable; +ObjcClassInfo *ObjCState::getClass(ClassDeclaration *decl) { + assert(!decl->isInterfaceDeclaration() && "Attempted to pass protocol into getClass!"); + if (classes.find(decl) != classes.end()) { + return &classes[decl]; } - auto classNameRo = getObjcClassRoSymbol(name, false); - auto metaNameRo = getObjcClassRoSymbol(name, true); + auto name = objcResolveName(decl); - // If we were weakly declared before, go grab our declarations. - // Otherwise, create all the base tables for the type. - classTable = getOrCreate(className, ObjcClass::getObjcClassType(module), OBJC_SECNAME_DATA); - metaClassTable = getOrCreate(metaName, ObjcClass::getObjcClassType(module), OBJC_SECNAME_DATA); - classRoTable = getOrCreate(classNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); - metaClassRoTable = getOrCreate(metaNameRo, ObjcClass::getObjcClassRoType(module), OBJC_SECNAME_CONST); - - classref = makeGlobalRef(classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS, false, true); - this->scan(); - - // Emit their structure. - this->emitName(); - this->emitTable(classTable, metaClassTable, getSuper(false), classRoTable); - this->emitTable(metaClassTable, getRootMetaClass(), getSuper(true), metaClassRoTable); - this->emitRoTable(classRoTable, false); - this->emitRoTable(metaClassRoTable, true); - - this->retain(classTable); - this->retain(metaClassTable); - this->retain(classRoTable); - this->retain(metaClassRoTable); - this->retain(classref); - return classTable; -} - -LLValue *ObjcClass::deref(LLType *as) { - if (decl->objc.isExtern && decl->objc.isSwiftStub) { - auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); - return DtoBitCast( - gIR->CreateCallOrInvoke(loadClassFunc, classref, ""), - as - ); - } + // Since we may end up referring to this very quickly + // the name should be assigned ASAP. + classes[decl] = { + .decl = decl, + .name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME) + }; + auto& classInfo = classes[decl]; + - return DtoLoad(as, classref); -} + auto className = objcGetClassSymbol(name, false); + auto metaName = objcGetClassSymbol(name, true); + if (decl->objc.isExtern) { -LLConstant *ObjcClass::ref() { - return classref; -} + classInfo.classTable = makeGlobal(className, objcGetClassType(module), "", true, false); + classInfo.metaclassTable = makeGlobal(metaName, objcGetClassType(module), "", true, false); + classInfo.ref = makeGlobalRef(classInfo.classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); + + this->retain(classInfo.ref); + this->retain(classInfo.metaclassTable); + this->retain(classInfo.classTable); + this->retain(classInfo.name); + return &classes[decl]; + } -LLConstant *ObjcClass::get() { - return classTable; + // If we were weakly declared before, go grab our declarations. + // Otherwise, create all the base tables for the type. + classInfo.classTable = getClassTable(decl); + classInfo.metaclassTable = getClassTable(decl->objc.metaclass); + classInfo.ref = makeGlobalRef(classInfo.classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); + + this->retain(classInfo.ref); + this->retain(classInfo.metaclassTable); + this->retain(classInfo.classTable); + this->retain(classInfo.name); + return &classes[decl]; } // // PROTOCOLS // -LLConstant *ObjcProtocol::emitTable() { +LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { LLConstantList members; LLGlobalVariable *protocolList = nullptr; LLGlobalVariable *classMethodList = nullptr; @@ -715,19 +583,20 @@ LLConstant *ObjcProtocol::emitTable() { LLGlobalVariable *optClassMethodList = nullptr; LLGlobalVariable *optInstanceMethodList = nullptr; - this->scan(); + auto& protoInfo = protocols[decl]; + auto name = objcResolveName(decl); // Base Protocols - if (auto baseProtocols = this->emitProtocolList()) { - auto sym = getObjcProtoListSymbol(getName(), true); + if (auto baseProtocols = createProtocolList(decl)) { + auto sym = objcGetProtoListSymbol(name, true); protocolList = getOrCreateWeak(sym, baseProtocols->getType(), OBJC_SECNAME_CONST); protocolList->setInitializer(baseProtocols); } // Class methods - if (auto classMethodConsts = this->emitMethodList(classMethods)) { - auto sym = getObjcProtoMethodListSymbol(getName(), true, false); + if (auto classMethodConsts = createMethodList(decl, true, false)) { + auto sym = objcGetProtoMethodListSymbol(name, true, false); classMethodList = makeGlobal(sym, classMethodConsts->getType(), OBJC_SECNAME_CONST); classMethodList->setInitializer(classMethodConsts); @@ -736,8 +605,8 @@ LLConstant *ObjcProtocol::emitTable() { } // Instance methods - if (auto instanceMethodConsts = this->emitMethodList(instanceMethods)) { - auto sym = getObjcProtoMethodListSymbol(getName(), false, false); + if (auto instanceMethodConsts = createMethodList(decl, false, false)) { + auto sym = objcGetProtoMethodListSymbol(name, false, false); instanceMethodList = makeGlobal(sym, instanceMethodConsts->getType(), OBJC_SECNAME_CONST); instanceMethodList->setInitializer(instanceMethodConsts); @@ -746,8 +615,8 @@ LLConstant *ObjcProtocol::emitTable() { } // Optional class methods - if (auto optClassMethodConsts = this->emitMethodList(classMethods, true)) { - auto sym = getObjcProtoMethodListSymbol(getName(), true, true); + if (auto optClassMethodConsts = createMethodList(decl, true, true)) { + auto sym = objcGetProtoMethodListSymbol(name, true, true); optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST); optClassMethodList->setInitializer(optClassMethodConsts); @@ -756,8 +625,8 @@ LLConstant *ObjcProtocol::emitTable() { } // Optional instance methods - if (auto optInstanceMethodConsts = this->emitMethodList(instanceMethods, true)) { - auto sym = getObjcProtoMethodListSymbol(getName(), false, true); + if (auto optInstanceMethodConsts = createMethodList(decl, false, true)) { + auto sym = objcGetProtoMethodListSymbol(name, false, true); optInstanceMethodList = makeGlobal(sym, optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); optInstanceMethodList->setInitializer(optInstanceMethodConsts); @@ -765,11 +634,11 @@ LLConstant *ObjcProtocol::emitTable() { optInstanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } - auto protoType = getObjcProtocolType(module); + auto protoType = objcGetProtocolType(module); auto allocSize = getTypeAllocSize(protoType); members.push_back(getNullPtr()); // isa - members.push_back(this->emitName()); // mangledName + members.push_back(protoInfo.name); // mangledName members.push_back(wrapNull(protocolList)); // protocols members.push_back(wrapNull(instanceMethodList)); // instanceMethods members.push_back(wrapNull(classMethodList)); // classMethods @@ -785,154 +654,217 @@ LLConstant *ObjcProtocol::emitTable() { ); } -LLConstant *ObjcProtocol::emit() { - if (protocolTable) - return protocolTable; +ObjcProtocolInfo *ObjCState::getProtocol(InterfaceDeclaration *decl) { + assert(decl->isInterfaceDeclaration() && "Attempted to pass class into getProtocol!"); + if (protocols.find(decl) != protocols.end()) { + return &protocols[decl]; + } + protocols[decl] = { .decl = decl }; + auto& protoInfo = protocols[decl]; - auto name = getName(); - auto protoName = getObjcProtoSymbol(name); - auto protoLabel = getObjcProtoLabelSymbol(name); + auto name = objcResolveName(decl); + auto protoName = objcGetProtoSymbol(name); + auto protoLabel = objcGetProtoLabelSymbol(name); - // Emit their structure. - auto protoTableConst = this->emitTable(); + protoInfo.name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); // We want it to be locally hidden and weak since the protocols // may be declared in multiple object files. - protocolTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); - protocolTable->setInitializer(protoTableConst); + auto protoTableConst = createProtocolTable(decl); + protoInfo.protoTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); + protoInfo.protoTable->setInitializer(protoTableConst); - protoref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST); - protoref->setInitializer(protocolTable); + protoInfo.ref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST); + protoInfo.ref->setInitializer(protoInfo.protoTable); - this->retain(protocolTable); - this->retain(protoref); - return protocolTable; + this->retain(protoInfo.protoTable); + this->retain(protoInfo.ref); + return &protocols[decl]; } -LLValue *ObjcProtocol::deref(LLType *as) { - return DtoLoad(as, protoref); -} +LLConstant *ObjCState::createProtocolList(ClassDeclaration *decl) { + LLConstantList protoList; + auto ifaces = decl->interfaces; + // Protocols + for(size_t i = 0; i < ifaces.length; i++) { + if (auto iface = ifaces.ptr[i]) { + if (auto ifacesym = (InterfaceDeclaration *)iface->sym) { + + // Only add interfaces which have objective-c linkage + // TODO: throw an error if you try to include a non-objective-c interface? + if (ifacesym->classKind == ClassKind::objc) { + if (auto proto = getProtocol(ifacesym)) { + protoList.push_back(proto->protoTable); + } + } + } + } + } -LLConstant *ObjcProtocol::ref() { - return protoref; + return objcEmitList(module, protoList, true, true); } // -// STATE +// METHODS // -ObjcClass *ObjCState::getClassRef(ClassDeclaration *cd) { - if (auto id = cd->isInterfaceDeclaration()) - return nullptr; - auto classList = this->classes; - if (!classList.empty()) { - for(auto it : classList) { - if (it->decl == cd) { - return it; - } - } +ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) { + if (methods.find(decl) != methods.end()) { + return &methods[decl]; } - auto klass = new ObjcClass(module, *this, cd); - classes.push_back(klass); - klass->get(); - return klass; -} + // Skip functions not marked as extern(Objective-C). + if (decl->_linkage != LINK::objc) + return nullptr; + + methods[decl] = { .decl = decl }; + auto& methodInfo = methods[decl]; -ObjcProtocol *ObjCState::getProtocolRef(InterfaceDeclaration *id) { + auto name = objcResolveName(decl); + methodInfo.name = makeGlobalStr(name, "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + methodInfo.type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + methodInfo.selector = makeGlobalRef(methodInfo.name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); + + methodInfo.llfunction = decl->fbody ? + DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : + getNullPtr(); - auto protoList = this->protocols; - if (!protoList.empty()) { - for(auto it : protoList) { - if (it->decl == id) { - return it; - } - } - } + this->retain(methodInfo.name); + this->retain(methodInfo.type); + this->retain(methodInfo.selector); + return &methods[decl]; +} - auto proto = new ObjcProtocol(module, *this, id); - protocols.push_back(proto); - proto->get(); - return proto; +LLConstant *ObjCState::createMethodInfo(FuncDeclaration *decl) { + auto method = getMethod(decl); + return LLConstantStruct::get( + objcGetMethodType(module), + { method->name, method->type, method->llfunction } + ); } -ObjcMethod *ObjCState::getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd) { - if (auto id = cd->isInterfaceDeclaration()) { - if (auto proto = getProtocolRef(id)) { +LLConstant *ObjCState::createMethodList(ClassDeclaration *decl, bool meta, bool optional) { + auto methodDeclList = getMethodsForType(meta ? decl : decl->objc.metaclass, optional); - // Attempt to get the method, if not found - // try the parent. - auto method = proto->getMethod(fd); - if (!method) { - for (auto baseclass : *id->baseclasses) { - method = getMethodRef(baseclass->sym, fd); + LLConstantList methodList; + for(auto func : methodDeclList) { + methodList.push_back(createMethodInfo(func)); + } + return objcEmitList(module, methodList, false); +} - if (method) - break; - } - } - return method; - } - } else if (auto klass = getClassRef(cd)) { - // Attempt to get the method, if not found - // try the parent. - auto method = klass->getMethod(fd); - if (!method) { - for (auto baseclass : *id->baseclasses) { - method = getMethodRef(baseclass->sym, fd); +// +// INSTANCE VARIABLES +// - if (method) - break; - } +ObjcIvarInfo *ObjCState::getIvar(VarDeclaration *decl) { + if (ivars.find(decl) != ivars.end()) { + return &ivars[decl]; + } + + if (auto klass = decl->parent->isClassDeclaration()) { + auto ivarsym = objcGetIvarSymbol(objcResolveName(decl->parent), objcResolveName(decl)); + ivars[decl] = { .decl = decl }; + auto& ivarInfo = ivars[decl]; + + // Extern classes should generate globals + // which can be filled out by the Objective-C runtime. + if (klass->objc.isExtern) { + ivarInfo.name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true); + ivarInfo.type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true); + + // It will be filled out by the runtime, but make sure it's there nontheless. + ivarInfo.offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + ivarInfo.offset->setInitializer(objcOffsetIvar(0)); + + this->retain(ivarInfo.name); + this->retain(ivarInfo.type); + this->retain(ivarInfo.offset); + return &ivars[decl]; } - return method; + // Non-extern ivars should emit all the data so that the + // objective-c runtime has a starting point. + // the offset *WILL* change during runtime! + ivarInfo.name = makeGlobalStr(decl->ident->toChars(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + ivarInfo.type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + ivarInfo.offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + ivarInfo.offset->setInitializer(objcOffsetIvar(decl->offset)); + + this->retain(ivarInfo.name); + this->retain(ivarInfo.type); + this->retain(ivarInfo.offset); + return &ivars[decl]; } return nullptr; } -ObjcMethod *ObjCState::getMethodRef(FuncDeclaration *fd) { - if (auto cd = fd->parent->isClassDeclaration()) { - if (auto retval = getMethodRef(cd, fd)) - return retval; - } +LLConstant *ObjCState::createIvarInfo(VarDeclaration *decl) { + auto ivar = getIvar(decl); + LLConstantList members; - return nullptr; + members.push_back(ivar->offset); + members.push_back(ivar->name); + members.push_back(ivar->type); + members.push_back(DtoConstUint(decl->alignment.isDefault() ? -1 : decl->alignment.get())); + members.push_back(DtoConstUint(decl->size(decl->loc))); + + return LLConstantStruct::get( + objcGetIvarType(module), + members + ); } -ObjcIvar *ObjCState::getIVarRef(ClassDeclaration *cd, VarDeclaration *vd) { - if (auto klass = getClassRef(cd)) { - return klass->get(vd); +LLConstant *ObjCState::createIvarList(ClassDeclaration *decl) { + LLConstantList ivarList; + + for(auto field : decl->fields) { + ivarList.push_back(createIvarInfo(field)); } - - return nullptr; + return objcEmitList(module, ivarList, false); } -LLGlobalVariable *ObjCState::getIVarOffset(ClassDeclaration *cd, VarDeclaration *vd) { - if (auto klass = getClassRef(cd)) { - return klass->getIVarOffset(vd); +// +// HELPERS +// +LLValue *ObjCState::deref(ClassDeclaration *decl, LLType *as) { + + // Protocols can also have static functions + // as such we need to also be able to dereference them. + if (auto proto = decl->isInterfaceDeclaration()) { + return DtoLoad(as, getProtocol(proto)->ref); + } + + // Classes may be class stubs. + // in that case, we need to call objc_loadClassRef instead of just + // loading from the classref. + auto classref = getClass(decl)->ref; + if (decl->objc.isExtern && decl->objc.isSwiftStub) { + auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef"); + return DtoBitCast( + gIR->CreateCallOrInvoke(loadClassFunc, classref, ""), + as + ); } - return nullptr; + return DtoLoad(as, classref); } -void ObjCState::emit(ClassDeclaration *cd) { +ObjcList ObjCState::getMethodsForType(ClassDeclaration *decl, bool optional) { + ObjcList funcs; - // Protocols should more or less always be emitted. - if (auto id = cd->isInterfaceDeclaration()) { - getProtocolRef(id); - return; + if (decl) { + for(size_t i = 0; i < decl->objc.methodList.length; i++) { + auto method = decl->objc.methodList.ptr[i]; + + if (method->objc.isOptional == optional) + funcs.push_back(method); + } } - - // Meta-classes are emitted automatically with the class, - // as such we only need to emit a classref for the base class. - if (!cd || cd->objc.isMeta) - return; - - getClassRef(cd); + return funcs; } // @@ -940,14 +872,16 @@ void ObjCState::emit(ClassDeclaration *cd) { // void ObjCState::finalize() { - size_t totalObjects = retained.size(); - if (totalObjects > 0) { - - genImageInfo(); + if (retainedSymbols.size() > 0) { retainSymbols(); + genImageInfo(); } } +void ObjCState::retain(LLConstant *symbol) { + retainedSymbols.push_back(symbol); +} + void ObjCState::genImageInfo() { module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2u); // Only support ABI 2. (Non-fragile) module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version @@ -956,8 +890,6 @@ void ObjCState::genImageInfo() { } void ObjCState::retainSymbols() { - auto retainedSymbols = this->retained; - if (!retainedSymbols.empty()) { auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), retainedSymbols.size()); diff --git a/gen/objcgen.h b/gen/objcgen.h index c5e8a87ecab..854a46a5f57 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -70,477 +70,132 @@ class ObjCState; #define OBJC_STRUCTNAME_METHOD "objc_method" #define ObjcList std::vector - -// Gets the Objective-C type encoding for D type t -std::string getTypeEncoding(Type *t); +#define ObjcMap std::unordered_map // Gets whether Objective-C is supported. bool objc_isSupported(const llvm::Triple &triple); // Generate name strings -std::string getObjcClassRoSymbol(const char *name, bool meta); -std::string getObjcClassSymbol(const char *name, bool meta); -std::string getObjcClassLabelSymbol(const char *name); -std::string getObjcClassMethodListSymbol(const char *className, bool meta); -std::string getObjcIvarListSymbol(const char *className); -std::string getObjcIvarSymbol(const char *className, const char *varName); -std::string getObjcProtoMethodListSymbol(const char *className, bool meta, bool optional); -std::string getObjcProtoSymbol(const char *name); -std::string getObjcProtoListSymbol(const char *name); -std::string getObjcSymbolName(const char *dsymPrefix, const char *dsymName); - - -// Base class for Objective-C definitions in a -// LLVM module. -class ObjcObject { -public: - ObjcObject(llvm::Module &module, ObjCState &objc) : module(module), objc(objc) { - this->emit(); - } - - // Gets a reference to the object in the module. - virtual LLConstant *get() { return nullptr; } +std::string objcGetClassRoSymbol(const char *name, bool meta); +std::string objcGetClassSymbol(const char *name, bool meta); +std::string objcGetClassLabelSymbol(const char *name); +std::string objcGetClassMethodListSymbol(const char *className, bool meta); +std::string objcGetIvarListSymbol(const char *className); +std::string objcGetIvarSymbol(const char *className, const char *varName); +std::string objcGetProtoMethodListSymbol(const char *className, bool meta, bool optional); +std::string objcGetProtoSymbol(const char *name); +std::string objcGetProtoListSymbol(const char *name); +std::string objcGetSymbolName(const char *dsymPrefix, const char *dsymName); + +// Utility which fetches the appropriate Objective-C +// name for a declaration. +const char *objcResolveName(Dsymbol *decl); - // Gets the name of the object. - virtual const char *getName() { return nullptr; } +// Gets the Objective-C type encoding for D type t +std::string objcGetTypeEncoding(Type *t); -protected: +// class_t +LLStructType *objcGetClassType(const llvm::Module& module); - // Gets a global variable or creates it. - LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false) { - auto global = module.getGlobalVariable(name, true); - if (global) - return global; +// class_ro_t +LLStructType *objcGetClassRoType(const llvm::Module& module); - return makeGlobal(name, type, section, true, extInitializer); - } +// stub_class_t +LLStructType *objcGetStubClassType(const llvm::Module& module); - // Gets a global variable or creates it. - LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false) { - auto global = module.getGlobalVariable(name, true); - if (global) - return global; +// objc_method +LLStructType *objcGetMethodType(const llvm::Module& module); - global = makeGlobal(name, type, section, false, extInitializer); - global->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); - global->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); - return global; - } +// ivar_t +LLStructType *objcGetIvarType(const llvm::Module& module); - // The module the object resides in. - llvm::Module &module; +// protocol_t +LLStructType *objcGetProtocolType(const llvm::Module& module); - // The irstate. - ObjCState &objc; +// xyz_list_t (count-only) +LLConstant *objcEmitList(llvm::Module &module, LLConstantList objects, bool alignSizeT = false, bool countOnly = false); - // Called to emit the data for the type. - virtual LLConstant *emit() { return nullptr; } +struct ObjcClassInfo { + ClassDeclaration *decl; + LLGlobalVariable *ref; - // Emits a new list for the specified objects as a constant. - static LLConstant *emitList(llvm::Module &module, LLConstantList objects, bool alignSizeT = false); + LLGlobalVariable *name; + LLGlobalVariable *classTable; + LLGlobalVariable *metaclassTable; +}; - // Emits a new list for the specified objects as a constant. - // This list only features count and not struct size. - static LLConstant *emitCountList(llvm::Module &module, LLConstantList objects, bool alignSizeT = false); +struct ObjcProtocolInfo { + InterfaceDeclaration *decl; + LLGlobalVariable *ref; - // Retains a symbol. - void retain(LLGlobalVariable *toRetain); + LLGlobalVariable *name; + LLGlobalVariable *protoTable; }; -// objc_method -class ObjcMethod : public ObjcObject { -public: +struct ObjcMethodInfo { FuncDeclaration *decl; - - ObjcMethod(llvm::Module &module, ObjCState &objc, FuncDeclaration *decl) : - ObjcObject(module, objc), decl(decl) { this->emit(); } - - // Gets the main reference to the object. - LLConstant *get() override; - - // Gets the type of an Objective-C objc_method struct - static LLStructType *getObjcMethodType(const llvm::Module& module) { - auto methType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_METHOD); - if (methType) - return methType; - - return LLStructType::create( - module.getContext(), - { - getOpaquePtrType(), // SEL name - getOpaquePtrType(), // const char *types - getOpaquePtrType(), // IMP imp - }, - OBJC_STRUCTNAME_METHOD - ); - } - - // Emits the constant struct containing the method - // information. - LLConstant *info(bool emitExtern=false); - - // Gets the selector for the function - LLStringRef getSelector() { - auto selector = decl->objc.selector; - return LLStringRef(selector->stringvalue, selector->stringlen); - } - - // Gets whether the function is optional. - bool isOptional() { - return decl->objc.isOptional; - } - -protected: - - // Called to emit the object. - LLConstant *emit() override; - -private: - LLGlobalVariable *selref; + LLGlobalVariable *name; LLGlobalVariable *type; - LLConstant *llfunc; + LLGlobalVariable *selector; + LLConstant *llfunction; }; -// ivar_t -class ObjcIvar : public ObjcObject { -public: +struct ObjcIvarInfo { VarDeclaration *decl; - ObjcIvar(llvm::Module &module, ObjCState &objc, VarDeclaration *decl) : - ObjcObject(module, objc), decl(decl) { this->emit(); } - - // Gets the type for an Objective-C ivar_t struct. - static LLStructType *getObjcIvarType(const llvm::Module& module) { - auto ivarType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_IVAR); - if (ivarType) - return ivarType; - - ivarType = LLStructType::create( - module.getContext(), - { - getOpaquePtrType(), // int32_t *offset - getOpaquePtrType(), // const char *name - getOpaquePtrType(), // const char *type - getI32Type(), // uint32_t alignment_raw - getI32Type(), // uint32_t size - }, - OBJC_STRUCTNAME_IVAR - ); - return ivarType; - } - - LLConstant *getOffset() { - return offset; - } - - // Gets the main reference to the object. - LLConstant *get() override { - return name; - } - - // Emits the constant struct containing the method - // information. - LLConstant *info(); - -protected: - - // Called to emit the object. - LLConstant *emit() override; - -private: - LLGlobalVariable *offset; LLGlobalVariable *name; LLGlobalVariable *type; -}; - -// Base type for class-like objective-c types. -class ObjcClasslike : public ObjcObject { -public: - ClassDeclaration *decl; - - ObjcClasslike(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcObject(module, objc), decl(decl) { this->emit(); } - - const char *getName() override; - - virtual ObjcMethod *getMethod(FuncDeclaration *fd) { - for(auto it : instanceMethods) { - if (it->decl == fd) { - return it; - } - } - - for(auto it : classMethods) { - if (it->decl == fd) { - return it; - } - } - - return nullptr; - } - - // Scans the class-like object and fills out internal information - // about functions, ivars, etc. - void scan() { - if (!hasScanned) - onScan(); - - hasScanned = true; - } - -protected: - LLGlobalVariable *emitName(); - - // Implement this to modify scanning behaviour. - virtual void onScan(); - - // Emits a method list as a constant. - LLConstant *emitMethodList(std::vector &methods, bool optionalMethods=false); - - // Emits a method list as a constant. - LLConstant *emitProtocolList(); - - ObjcList instanceMethods; - ObjcList classMethods; -private: - - LLGlobalVariable *className; - bool hasScanned; -}; - -// objc_protocol_t -class ObjcProtocol : public ObjcClasslike { -public: - ObjcProtocol(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcClasslike(module, objc, decl) { - this->emit(); - this->scan(); - } - - // Gets the type of an Objective-C class_t struct - static LLStructType *getObjcProtocolType(const llvm::Module& module) { - auto protoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_PROTO); - if (protoType) - return protoType; - - protoType = LLStructType::create( - module.getContext(), - { - getOpaquePtrType(), // objc_object* isa - getOpaquePtrType(), // protocol_list_t* protocols - getOpaquePtrType(), // const char *mangledName - getOpaquePtrType(), // method_list_t* instanceMethods - getOpaquePtrType(), // method_list_t* classMethods - getOpaquePtrType(), // method_list_t* optionalInstanceMethods - getOpaquePtrType(), // method_list_t* optionalClassMethods - getOpaquePtrType(), // property_list_t* instanceProperties - getI32Type(), // uint32_t size - getI32Type(), // uint32_t flags - - // Further fields follow but are optional and are fixed up at - // runtime. - }, - OBJC_STRUCTNAME_PROTO - ); - return protoType; - } - - // Gets a protocol by its name - static LLGlobalVariable *get(const llvm::Module& module, LLStringRef name, bool meta = false) { - return module.getGlobalVariable(getObjcProtoSymbol(name.data()), true); - } - - // Gets the main reference to the object. - LLConstant *get() override { - return protocolTable; - } - - // Gets the protocol ref. - LLConstant *ref(); - - // Gets the protocol ref. - LLValue *deref(LLType *as); - -protected: - - // Called to emit the object. - LLConstant *emit() override; - - // Emits the protocol table. - LLConstant *emitTable(); - -private: - LLGlobalVariable *protoref; - LLGlobalVariable *protocolTable; -}; - -// class_t, class_ro_t, stub_class_t -class ObjcClass : public ObjcClasslike { -public: - ObjcClass(llvm::Module &module, ObjCState &objc, ClassDeclaration *decl) : - ObjcClasslike(module, objc, decl) { - this->emit(); - this->scan(); - } - - // Gets objective-c the flags for the class declaration - static size_t getClassFlags(const ClassDeclaration& decl); - - // Gets the type of an Objective-C class_t struct - static LLStructType *getObjcClassType(const llvm::Module& module) { - auto classType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASS); - if (classType) - return classType; - - classType = LLStructType::create( - module.getContext(), - { - getOpaquePtrType(), // objc_object* isa - getOpaquePtrType(), // objc_object* superclass - getOpaquePtrType(), // cache_t* cache - getOpaquePtrType(), // void* vtbl; (unused, set to null) - getOpaquePtrType(), // class_ro_t* ro - }, - OBJC_STRUCTNAME_CLASS - ); - return classType; - } - - // Gets the type of an Objective-C class_ro_t struct. - static LLStructType *getObjcClassRoType(const llvm::Module& module) { - auto classRoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASSRO); - if (classRoType) - return classRoType; - - classRoType = LLStructType::create( - module.getContext(), - { - getI32Type(), // uint32_t flags - getI32Type(), // uint32_t instanceStart - getI32Type(), // uint32_t instanceSize - getOpaquePtrType(), // void* layoutOrNonMetaClass - getOpaquePtrType(), // const char* name - getOpaquePtrType(), // method_list_t* baseMethods - getOpaquePtrType(), // protocol_list_t* baseProtocols - getOpaquePtrType(), // ivar_list_t* ivars - getOpaquePtrType(), // const uint8_t* weakIvarLayout - getOpaquePtrType(), // property_list_t* baseProperties - }, - OBJC_STRUCTNAME_CLASSRO - ); - return classRoType; - } - - // Gets the type for an Swift stub class - static LLStructType *getObjcStubClassType(const llvm::Module& module) { - auto stubClassType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_STUBCLASS); - if (stubClassType) - return stubClassType; - - stubClassType = LLStructType::create( - module.getContext(), - { - getOpaquePtrType(), // objc_object* isa - getOpaquePtrType(), // function pointer. - }, - OBJC_STRUCTNAME_STUBCLASS - ); - return stubClassType; - } - - // Gets a class by its name - static LLGlobalVariable *get(const llvm::Module& module, LLStringRef name, bool meta = false) { - return module.getGlobalVariable(getObjcClassSymbol(name.data(), meta), true); - } - - ObjcIvar *get(VarDeclaration *vd) { - for(size_t i = 0; i < ivars.size(); i++) { - if (ivars[i]->decl == vd) { - return ivars[i]; - } - } - - return nullptr; - } - - LLGlobalVariable *getIVarOffset(VarDeclaration *vd); - - // Gets the class ref. - LLConstant *ref(); - - // Dereferences the class. - LLValue *deref(LLType *as); - - // Gets the main reference to the object. - LLConstant *get() override; - - // Gets the superclass of this class. - LLConstant *getSuper(bool meta); - - // Gets the root metaclass of this class. - LLConstant *getRootMetaClass(); - -protected: - - // Called to emit the object. - LLConstant *emit() override; - - void onScan() override; - -private: - ObjcList ivars; - - // Core data - ptrdiff_t getInstanceStart(bool meta); - size_t getInstanceSize(bool meta); - - void emitTable(LLGlobalVariable *table, LLConstant *super, LLConstant *meta, LLConstant *roTable); - void emitRoTable(LLGlobalVariable *table, bool meta); - - // instance variables - LLConstant *emitIvarList(); - - // Gets the empty cache variable, and creates a reference to it - // if needed. - LLGlobalVariable *getEmptyCache() { - static LLGlobalVariable *objcCache; - if(!objcCache) - objcCache = makeGlobal("_objc_empty_cache", nullptr, "", true, false); - return objcCache; - } - - LLGlobalVariable *classref; - LLGlobalVariable *classTable; - LLGlobalVariable *classRoTable; - LLGlobalVariable *metaClassTable; - LLGlobalVariable *metaClassRoTable; + LLGlobalVariable *offset; }; // Objective-C state tied to an LLVM module (object file). class ObjCState { -friend ObjcObject; public: ObjCState(llvm::Module &module) : module(module) { } - - void emit(ClassDeclaration *cd); - ObjcClass *getClassRef(ClassDeclaration *cd); - ObjcProtocol *getProtocolRef(InterfaceDeclaration *id); - ObjcMethod *getMethodRef(ClassDeclaration *cd, FuncDeclaration *fd); - ObjcMethod *getMethodRef(FuncDeclaration *fd); - ObjcIvar *getIVarRef(ClassDeclaration *cd, VarDeclaration *vd); - LLGlobalVariable *getIVarOffset(ClassDeclaration *cd, VarDeclaration *vd); + + ObjcClassInfo *getClass(ClassDeclaration *decl); + ObjcProtocolInfo *getProtocol(InterfaceDeclaration *decl); + ObjcMethodInfo *getMethod(FuncDeclaration *decl); + ObjcIvarInfo *getIvar(VarDeclaration *decl); + + LLValue *deref(ClassDeclaration *decl, LLType *as); + LLGlobalVariable *getClassRoTable(ClassDeclaration *decl); + LLGlobalVariable *getClassTable(ClassDeclaration *decl); void finalize(); private: llvm::Module &module; - ObjcList retained; - - ObjcList protocols; - ObjcList classes; + // Creates an ivar_t struct which can be + // used in ivar lists. + ObjcMap ivars; + LLConstant *createIvarInfo(VarDeclaration *decl); + LLConstant *createIvarList(ClassDeclaration *decl); + + // Creates an objc_method struct which can be + // used in method lists. + ObjcMap methods; + LLConstant *createMethodInfo(FuncDeclaration *decl); + LLConstant *createMethodList(ClassDeclaration *decl, bool meta, bool optional); + + // class_t and class_ro_t generation. + ObjcMap classes; + ObjcMap classTables; + ObjcMap classRoTables; + + // protocol_t generation. + ObjcMap protocols; + LLConstant *createProtocolTable(InterfaceDeclaration *decl); + LLConstant *createProtocolList(ClassDeclaration *decl); + + // Private helpers + ObjcList getMethodsForType(ClassDeclaration *decl, bool optional); + + ObjcList retainedSymbols; + void retain(LLConstant *symbol); void genImageInfo(); void retainSymbols(); }; diff --git a/gen/tocall.cpp b/gen/tocall.cpp index ac9723dcc45..6e2a9cceeeb 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -744,7 +744,7 @@ class ImplicitArgumentsBuilder { // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( DtoBitCast(dfnval->vthis, argtype), - gIR->objc.getClassRef(cls)->deref(getOpaquePtrType()), + gIR->objc.deref(cls, getOpaquePtrType()), "super" ); @@ -794,8 +794,7 @@ class ImplicitArgumentsBuilder { if (irFty.arg_objcSelector) { assert(dfnval); - auto method = gIR->objc.getMethodRef(dfnval->func); - auto methodptr = method->get(); + auto methodptr = gIR->objc.getMethod(dfnval->func)->selector; args.push_back(DtoLoad(methodptr->getType(), methodptr)); } } diff --git a/gen/toir.cpp b/gen/toir.cpp index f410bcd016e..53b27d6cce0 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -334,22 +334,18 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; - auto loadtype = DtoType(e->type); + auto lType = DtoType(e->type); if (auto iface = e->classDeclaration->isInterfaceDeclaration()) { // Protocols - if (auto proto = gIR->objc.getProtocolRef(iface)) { - result = new DImValue(e->type, proto->deref(loadtype)); - return; - } + result = new DImValue(e->type, gIR->objc.deref(iface, lType)); + return; } else { // Classes - if (auto klass = gIR->objc.getClassRef(e->classDeclaration)) { - result = new DImValue(e->type, klass->deref(loadtype)); - return; - } + result = new DImValue(e->type, gIR->objc.deref(e->classDeclaration, lType)); + return; } llvm_unreachable("Unknown type for ObjcClassReferenceExp."); diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index c650596751f..0f0240b857d 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -776,6 +776,25 @@ LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name, LLStringRef return var; } +LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer) { + auto global = gIR->module.getGlobalVariable(name, true); + if (global) + return global; + + return makeGlobal(name, type, section, true, extInitializer); +} + +LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer) { + auto global = gIR->module.getGlobalVariable(name, true); + if (global) + return global; + + global = makeGlobal(name, type, section, false, extInitializer); + global->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + global->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); + return global; +} + //////////////////////////////////////////////////////////////////////////////// LLType *getI8Type() { return LLType::getInt8Ty(gIR->context()); } diff --git a/gen/tollvm.h b/gen/tollvm.h index 240c6ee2434..5dd04e05994 100644 --- a/gen/tollvm.h +++ b/gen/tollvm.h @@ -152,6 +152,8 @@ LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type = nullptr, LLStringR LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedContents, LLStructType* type = nullptr, bool extern_ = false, bool externInit = false); LLGlobalVariable *makeGlobalRef(LLGlobalVariable *to, LLStringRef name = "", LLStringRef section = "", bool extern_ = false, bool externInit = false); LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name = "", LLStringRef section = "", bool extern_ = false, bool externInit = false); +LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false); +LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false); // llvm::T::get(...) wrappers LLType *getI8Type(); From e6d654826d7bce77eb11938523443f35bdbddf2e Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 10:54:19 +0100 Subject: [PATCH 61/67] Fix null references in class and method lookup --- gen/objcgen.cpp | 192 ++++++++++++++++++++++++------------------------ gen/objcgen.h | 4 +- 2 files changed, 100 insertions(+), 96 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 5773819304a..3a085eabbcc 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -364,26 +364,31 @@ LLConstant *objcOffsetIvar(size_t ivaroffset) { return DtoConstUint(getPointerSize()+ivaroffset); } -size_t objcGetClassFlags(const ClassDeclaration& decl) { +size_t objcGetClassFlags(ClassDeclaration *decl) { size_t flags = 0; - if (!decl.baseClass) + if (!decl->baseClass) flags |= RO_ROOT; - if (decl.objc.isMeta) + if (decl->objc.isMeta) flags |= RO_META; return flags; } -ClassDeclaration *objcGetRootMetaClass(ClassDeclaration *decl) { - auto curr = decl; - while (curr->baseClass) - curr = curr->baseClass; +ClassDeclaration *objcGetMetaClass(ClassDeclaration *decl) { + if (decl->objc.isMeta) { + auto curr = decl; + while (curr->baseClass) + curr = curr->baseClass; - return curr; + return curr; + } + + // Meta class for normal class. + return decl->objc.metaclass; } -ClassDeclaration *objcGetSuper(ClassDeclaration *decl, bool meta) { +ClassDeclaration *objcGetSuper(ClassDeclaration *decl) { return (decl->objc.isRootClass() || !decl->baseClass) ? decl : decl->baseClass; @@ -432,11 +437,14 @@ LLGlobalVariable *getEmptyCache() { return objcCache; } -LLGlobalVariable *ObjCState::getClassRoTable(ClassDeclaration *decl) { - if (classRoTables.find(decl) != classRoTables.end()) { - return classRoTables[decl]; +LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { + if (auto it = classRoTables.find(decl); it != classRoTables.end()) { + return it->second; } + if (!decl) + return getNullPtr(); + // No need to generate RO tables for externs. if (decl->objc.isExtern) return nullptr; @@ -474,7 +482,7 @@ LLGlobalVariable *ObjCState::getClassRoTable(ClassDeclaration *decl) { } // Build struct. - members.push_back(DtoConstUint(objcGetClassFlags(meta ? *decl->objc.metaclass : *decl))); + members.push_back(DtoConstUint(objcGetClassFlags(decl))); members.push_back(DtoConstUint(objcGetInstanceStart(module, decl, meta))); members.push_back(DtoConstUint(objcGetInstanceSize(module, decl, meta))); members.push_back(getNullPtr()); @@ -487,48 +495,49 @@ LLGlobalVariable *ObjCState::getClassRoTable(ClassDeclaration *decl) { auto table = makeGlobalWithBytes(sym, members, objcGetClassRoType(module)); table->setSection(OBJC_SECNAME_DATA); + this->retain(table); classRoTables[decl] = table; - this->retain(table); return table; } -LLGlobalVariable *ObjCState::getClassTable(ClassDeclaration *decl) { - if (classTables.find(decl) != classTables.end()) { - return classTables[decl]; +LLConstant *ObjCState::getClassTable(ClassDeclaration *decl) { + if (auto it = classTables.find(decl); it != classTables.end()) { + return it->second; } + + if (!decl) + return getNullPtr(); - auto meta = decl->objc.isMeta; + auto isExtern = decl->objc.isExtern; + auto isMeta = decl->objc.isMeta; auto name = objcResolveName(decl); - auto sym = objcGetClassSymbol(name, meta); + auto sym = objcGetClassSymbol(name, isMeta); + + auto table = getOrCreate(sym, objcGetClassType(module), OBJC_SECNAME_DATA, isExtern); + this->retain(table); + classTables[decl] = table; // Extern tables don't need a body. - if (decl->objc.isExtern) { - auto table = getOrCreateWeak(sym, objcGetClassType(module), "", true); - - classTables[decl] = table; - this->retain(table); + if (decl->objc.isExtern) return table; - } LLConstantList members; - members.push_back(getClassTable(meta ? decl : decl->objc.metaclass)); // isa - members.push_back(getClassTable(objcGetSuper(decl, meta))); // super - members.push_back(getEmptyCache()); // cache - members.push_back(getNullPtr()); // vtbl - members.push_back(getClassRoTable(decl)); // ro - - auto table = makeGlobalWithBytes(sym, members, objcGetClassType(module)); - table->setSection(OBJC_SECNAME_DATA); - - classTables[decl] = table; - this->retain(table); + members.push_back(getClassTable(objcGetMetaClass(decl))); // isa + members.push_back(getClassTable(objcGetSuper(decl))); // super + members.push_back(getEmptyCache()); // cache + members.push_back(getNullPtr()); // vtbl + members.push_back(getClassRoTable(decl)); // ro + table->setInitializer(LLConstantStruct::get( + objcGetClassType(module), + members + )); return table; } ObjcClassInfo *ObjCState::getClass(ClassDeclaration *decl) { assert(!decl->isInterfaceDeclaration() && "Attempted to pass protocol into getClass!"); - if (classes.find(decl) != classes.end()) { + if (auto it = classes.find(decl); it != classes.end()) { return &classes[decl]; } @@ -540,34 +549,29 @@ ObjcClassInfo *ObjCState::getClass(ClassDeclaration *decl) { .decl = decl, .name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME) }; - auto& classInfo = classes[decl]; - + auto classInfo = &classes[decl]; - auto className = objcGetClassSymbol(name, false); - auto metaName = objcGetClassSymbol(name, true); if (decl->objc.isExtern) { - classInfo.classTable = makeGlobal(className, objcGetClassType(module), "", true, false); - classInfo.metaclassTable = makeGlobal(metaName, objcGetClassType(module), "", true, false); - classInfo.ref = makeGlobalRef(classInfo.classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); + auto className = objcGetClassSymbol(name, false); + auto metaName = objcGetClassSymbol(name, true); + classInfo->classTable = makeGlobal(className, objcGetClassType(module), "", true, false); + classInfo->metaclassTable = makeGlobal(metaName, objcGetClassType(module), "", true, false); + classInfo->ref = makeGlobalRef(classInfo->classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); - this->retain(classInfo.ref); - this->retain(classInfo.metaclassTable); - this->retain(classInfo.classTable); - this->retain(classInfo.name); + this->retain(classInfo->ref); + this->retain(classInfo->name); return &classes[decl]; } // If we were weakly declared before, go grab our declarations. // Otherwise, create all the base tables for the type. - classInfo.classTable = getClassTable(decl); - classInfo.metaclassTable = getClassTable(decl->objc.metaclass); - classInfo.ref = makeGlobalRef(classInfo.classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); - - this->retain(classInfo.ref); - this->retain(classInfo.metaclassTable); - this->retain(classInfo.classTable); - this->retain(classInfo.name); + classInfo->classTable = (LLGlobalVariable *)getClassTable(decl); + classInfo->metaclassTable = (LLGlobalVariable *)getClassTable(decl->objc.metaclass); + classInfo->ref = makeGlobalRef(classInfo->classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); + + this->retain(classInfo->ref); + this->retain(classInfo->name); return &classes[decl]; } @@ -583,7 +587,7 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { LLGlobalVariable *optClassMethodList = nullptr; LLGlobalVariable *optInstanceMethodList = nullptr; - auto& protoInfo = protocols[decl]; + auto protoInfo = &protocols[decl]; auto name = objcResolveName(decl); // Base Protocols @@ -638,7 +642,7 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { auto allocSize = getTypeAllocSize(protoType); members.push_back(getNullPtr()); // isa - members.push_back(protoInfo.name); // mangledName + members.push_back(protoInfo->name); // mangledName members.push_back(wrapNull(protocolList)); // protocols members.push_back(wrapNull(instanceMethodList)); // instanceMethods members.push_back(wrapNull(classMethodList)); // classMethods @@ -656,29 +660,29 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { ObjcProtocolInfo *ObjCState::getProtocol(InterfaceDeclaration *decl) { assert(decl->isInterfaceDeclaration() && "Attempted to pass class into getProtocol!"); - if (protocols.find(decl) != protocols.end()) { + if (auto it = protocols.find(decl); it != protocols.end()) { return &protocols[decl]; } protocols[decl] = { .decl = decl }; - auto& protoInfo = protocols[decl]; + auto protoInfo = &protocols[decl]; auto name = objcResolveName(decl); auto protoName = objcGetProtoSymbol(name); auto protoLabel = objcGetProtoLabelSymbol(name); - protoInfo.name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); + protoInfo->name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); // We want it to be locally hidden and weak since the protocols // may be declared in multiple object files. auto protoTableConst = createProtocolTable(decl); - protoInfo.protoTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); - protoInfo.protoTable->setInitializer(protoTableConst); + protoInfo->protoTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); + protoInfo->protoTable->setInitializer(protoTableConst); - protoInfo.ref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST); - protoInfo.ref->setInitializer(protoInfo.protoTable); + protoInfo->ref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST); + protoInfo->ref->setInitializer(protoInfo->protoTable); - this->retain(protoInfo.protoTable); - this->retain(protoInfo.ref); + this->retain(protoInfo->protoTable); + this->retain(protoInfo->ref); return &protocols[decl]; } @@ -710,7 +714,7 @@ LLConstant *ObjCState::createProtocolList(ClassDeclaration *decl) { // ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) { - if (methods.find(decl) != methods.end()) { + if (auto it = methods.find(decl); it != methods.end()) { return &methods[decl]; } @@ -719,20 +723,20 @@ ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) { return nullptr; methods[decl] = { .decl = decl }; - auto& methodInfo = methods[decl]; + auto methodInfo = &methods[decl]; auto name = objcResolveName(decl); - methodInfo.name = makeGlobalStr(name, "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); - methodInfo.type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); - methodInfo.selector = makeGlobalRef(methodInfo.name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); + methodInfo->name = makeGlobalStr(name, "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + methodInfo->type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + methodInfo->selector = makeGlobalRef(methodInfo->name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); - methodInfo.llfunction = decl->fbody ? + methodInfo->llfunction = decl->fbody ? DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : getNullPtr(); - this->retain(methodInfo.name); - this->retain(methodInfo.type); - this->retain(methodInfo.selector); + this->retain(methodInfo->name); + this->retain(methodInfo->type); + this->retain(methodInfo->selector); return &methods[decl]; } @@ -759,43 +763,43 @@ LLConstant *ObjCState::createMethodList(ClassDeclaration *decl, bool meta, bool // INSTANCE VARIABLES // -ObjcIvarInfo *ObjCState::getIvar(VarDeclaration *decl) { - if (ivars.find(decl) != ivars.end()) { +ObjcIvarInfo* ObjCState::getIvar(VarDeclaration *decl) { + if (auto it = ivars.find(decl); it != ivars.end()) { return &ivars[decl]; } if (auto klass = decl->parent->isClassDeclaration()) { auto ivarsym = objcGetIvarSymbol(objcResolveName(decl->parent), objcResolveName(decl)); ivars[decl] = { .decl = decl }; - auto& ivarInfo = ivars[decl]; + auto ivarInfo = &ivars[decl]; // Extern classes should generate globals // which can be filled out by the Objective-C runtime. if (klass->objc.isExtern) { - ivarInfo.name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true); - ivarInfo.type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true); + ivarInfo->name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true); + ivarInfo->type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true); // It will be filled out by the runtime, but make sure it's there nontheless. - ivarInfo.offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - ivarInfo.offset->setInitializer(objcOffsetIvar(0)); + ivarInfo->offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + ivarInfo->offset->setInitializer(objcOffsetIvar(0)); - this->retain(ivarInfo.name); - this->retain(ivarInfo.type); - this->retain(ivarInfo.offset); + this->retain(ivarInfo->name); + this->retain(ivarInfo->type); + this->retain(ivarInfo->offset); return &ivars[decl]; } // Non-extern ivars should emit all the data so that the // objective-c runtime has a starting point. // the offset *WILL* change during runtime! - ivarInfo.name = makeGlobalStr(decl->ident->toChars(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); - ivarInfo.type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); - ivarInfo.offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); - ivarInfo.offset->setInitializer(objcOffsetIvar(decl->offset)); - - this->retain(ivarInfo.name); - this->retain(ivarInfo.type); - this->retain(ivarInfo.offset); + ivarInfo->name = makeGlobalStr(decl->ident->toChars(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); + ivarInfo->type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + ivarInfo->offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR); + ivarInfo->offset->setInitializer(objcOffsetIvar(decl->offset)); + + this->retain(ivarInfo->name); + this->retain(ivarInfo->type); + this->retain(ivarInfo->offset); return &ivars[decl]; } diff --git a/gen/objcgen.h b/gen/objcgen.h index 854a46a5f57..578e1ae9a39 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -161,8 +161,6 @@ class ObjCState { ObjcIvarInfo *getIvar(VarDeclaration *decl); LLValue *deref(ClassDeclaration *decl, LLType *as); - LLGlobalVariable *getClassRoTable(ClassDeclaration *decl); - LLGlobalVariable *getClassTable(ClassDeclaration *decl); void finalize(); @@ -185,6 +183,8 @@ class ObjCState { ObjcMap classes; ObjcMap classTables; ObjcMap classRoTables; + LLConstant *getClassRoTable(ClassDeclaration *decl); + LLConstant *getClassTable(ClassDeclaration *decl); // protocol_t generation. ObjcMap protocols; From db91254dc8eb4cb2f73b6f4cde8ce9bc9a76a276 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 11:00:46 +0100 Subject: [PATCH 62/67] include unordered_map --- gen/objcgen.h | 1 + 1 file changed, 1 insertion(+) diff --git a/gen/objcgen.h b/gen/objcgen.h index 578e1ae9a39..01718c2a40d 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -14,6 +14,7 @@ #pragma once #include +#include #include "llvm/ADT/StringMap.h" #include "gen/tollvm.h" #include "dmd/mtype.h" From c9bed7af856dc4fddab4df5846b97eceab9ca89c Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 12:38:03 +0100 Subject: [PATCH 63/67] Get functionality on-par with prev impl. --- gen/objcgen.cpp | 166 ++++++++++++++++++++++++++---------------------- gen/objcgen.h | 15 +++-- 2 files changed, 100 insertions(+), 81 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 3a085eabbcc..6c43a2e1f66 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -442,13 +442,11 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { return it->second; } - if (!decl) + // No need to generate RO tables for externs. + // nor for null declarations. + if (!decl || decl->objc.isExtern) return getNullPtr(); - // No need to generate RO tables for externs. - if (decl->objc.isExtern) - return nullptr; - // Base Methods auto meta = decl->objc.isMeta; auto name = objcResolveName(decl); @@ -459,9 +457,7 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { LLGlobalVariable *protocolList = nullptr; LLGlobalVariable *methodList = nullptr; - // NOTE: createMethodList does not need to automatically select the meta-class - // in this instance. - if (auto baseMethods = createMethodList(decl, false, false)) { + if (auto baseMethods = createMethodList(decl, false)) { methodList = getOrCreate(objcGetClassMethodListSymbol(name, meta), baseMethods->getType(), OBJC_SECNAME_CONST); methodList->setInitializer(baseMethods); } @@ -473,7 +469,7 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { } if (!meta) { - + // Instance variables if (auto baseIvars = createIvarList(decl)) { ivarList = getOrCreate(objcGetIvarListSymbol(name), baseIvars->getType(), OBJC_SECNAME_CONST); @@ -486,7 +482,7 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { members.push_back(DtoConstUint(objcGetInstanceStart(module, decl, meta))); members.push_back(DtoConstUint(objcGetInstanceSize(module, decl, meta))); members.push_back(getNullPtr()); - members.push_back(getClass(decl)->name); + members.push_back(getClassName(decl)); members.push_back(wrapNull(methodList)); members.push_back(wrapNull(protocolList)); members.push_back(wrapNull(ivarList)); @@ -495,9 +491,9 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { auto table = makeGlobalWithBytes(sym, members, objcGetClassRoType(module)); table->setSection(OBJC_SECNAME_DATA); - this->retain(table); classRoTables[decl] = table; + this->retain(table); return table; } @@ -506,17 +502,16 @@ LLConstant *ObjCState::getClassTable(ClassDeclaration *decl) { return it->second; } + // If decl is null, just return a null pointer. if (!decl) return getNullPtr(); - auto isExtern = decl->objc.isExtern; - auto isMeta = decl->objc.isMeta; auto name = objcResolveName(decl); - auto sym = objcGetClassSymbol(name, isMeta); + auto sym = objcGetClassSymbol(name, decl->objc.isMeta); - auto table = getOrCreate(sym, objcGetClassType(module), OBJC_SECNAME_DATA, isExtern); - this->retain(table); + auto table = getOrCreate(sym, objcGetClassType(module), OBJC_SECNAME_DATA, decl->objc.isExtern); classTables[decl] = table; + this->retain(table); // Extern tables don't need a body. if (decl->objc.isExtern) @@ -541,40 +536,46 @@ ObjcClassInfo *ObjCState::getClass(ClassDeclaration *decl) { return &classes[decl]; } - auto name = objcResolveName(decl); - // Since we may end up referring to this very quickly // the name should be assigned ASAP. - classes[decl] = { - .decl = decl, - .name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME) - }; + classes[decl] = { .decl = decl }; auto classInfo = &classes[decl]; - if (decl->objc.isExtern) { + classInfo->table = (LLGlobalVariable *)getClassTable(decl); + classInfo->name = (LLGlobalVariable *)getClassName(decl); + classInfo->ref = (LLGlobalVariable *)getClassRef(decl); - auto className = objcGetClassSymbol(name, false); - auto metaName = objcGetClassSymbol(name, true); - classInfo->classTable = makeGlobal(className, objcGetClassType(module), "", true, false); - classInfo->metaclassTable = makeGlobal(metaName, objcGetClassType(module), "", true, false); - classInfo->ref = makeGlobalRef(classInfo->classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); + if (!decl->objc.isMeta) + classInfo->ref->setInitializer(classInfo->table); + + return classInfo; +} - this->retain(classInfo->ref); - this->retain(classInfo->name); - return &classes[decl]; +LLConstant *ObjCState::getClassName(ClassDeclaration *decl) { + LLStringRef className(objcResolveName(decl)); + if (auto it = classNames.find(className); it != classNames.end()) { + return it->second; } - // If we were weakly declared before, go grab our declarations. - // Otherwise, create all the base tables for the type. - classInfo->classTable = (LLGlobalVariable *)getClassTable(decl); - classInfo->metaclassTable = (LLGlobalVariable *)getClassTable(decl->objc.metaclass); - classInfo->ref = makeGlobalRef(classInfo->classTable, "OBJC_CLASSLIST_REFERENCES_$_", OBJC_SECNAME_CLASSREFS); + auto retval = makeGlobalStr(className, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); + classNames[className] = retval; + this->retain(retval); + return retval; +} - this->retain(classInfo->ref); - this->retain(classInfo->name); - return &classes[decl]; +LLConstant *ObjCState::getClassRef(ClassDeclaration *decl) { + LLStringRef className(objcResolveName(decl)); + if (auto it = classRefs.find(className); it != classRefs.end()) { + return it->second; + } + + auto retval = getOrCreate("OBJC_CLASSLIST_REFERENCES_$_", getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); + classRefs[className] = retval; + this->retain(retval); + return retval; } + // // PROTOCOLS // @@ -598,18 +599,8 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { protocolList->setInitializer(baseProtocols); } - // Class methods - if (auto classMethodConsts = createMethodList(decl, true, false)) { - auto sym = objcGetProtoMethodListSymbol(name, true, false); - classMethodList = makeGlobal(sym, classMethodConsts->getType(), OBJC_SECNAME_CONST); - classMethodList->setInitializer(classMethodConsts); - - classMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); - classMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); - } - // Instance methods - if (auto instanceMethodConsts = createMethodList(decl, false, false)) { + if (auto instanceMethodConsts = createMethodList(decl, false)) { auto sym = objcGetProtoMethodListSymbol(name, false, false); instanceMethodList = makeGlobal(sym, instanceMethodConsts->getType(), OBJC_SECNAME_CONST); instanceMethodList->setInitializer(instanceMethodConsts); @@ -618,18 +609,8 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { instanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } - // Optional class methods - if (auto optClassMethodConsts = createMethodList(decl, true, true)) { - auto sym = objcGetProtoMethodListSymbol(name, true, true); - optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST); - optClassMethodList->setInitializer(optClassMethodConsts); - - optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); - optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); - } - // Optional instance methods - if (auto optInstanceMethodConsts = createMethodList(decl, false, true)) { + if (auto optInstanceMethodConsts = createMethodList(decl, true)) { auto sym = objcGetProtoMethodListSymbol(name, false, true); optInstanceMethodList = makeGlobal(sym, optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST); optInstanceMethodList->setInitializer(optInstanceMethodConsts); @@ -637,12 +618,32 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) { optInstanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); optInstanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); } + + // Class methods + if (auto classMethodConsts = createMethodList(decl->objc.metaclass, false)) { + auto sym = objcGetProtoMethodListSymbol(name, true, false); + classMethodList = makeGlobal(sym, classMethodConsts->getType(), OBJC_SECNAME_CONST); + classMethodList->setInitializer(classMethodConsts); + + classMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + classMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); + } + + // Optional class methods + if (auto optClassMethodConsts = createMethodList(decl->objc.metaclass, true)) { + auto sym = objcGetProtoMethodListSymbol(name, true, true); + optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST); + optClassMethodList->setInitializer(optClassMethodConsts); + + optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage); + optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility); + } auto protoType = objcGetProtocolType(module); auto allocSize = getTypeAllocSize(protoType); members.push_back(getNullPtr()); // isa - members.push_back(protoInfo->name); // mangledName + members.push_back(protoInfo->name); // mangledName members.push_back(wrapNull(protocolList)); // protocols members.push_back(wrapNull(instanceMethodList)); // instanceMethods members.push_back(wrapNull(classMethodList)); // classMethods @@ -663,27 +664,27 @@ ObjcProtocolInfo *ObjCState::getProtocol(InterfaceDeclaration *decl) { if (auto it = protocols.find(decl); it != protocols.end()) { return &protocols[decl]; } + protocols[decl] = { .decl = decl }; auto protoInfo = &protocols[decl]; auto name = objcResolveName(decl); auto protoName = objcGetProtoSymbol(name); auto protoLabel = objcGetProtoLabelSymbol(name); - protoInfo->name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME); // We want it to be locally hidden and weak since the protocols // may be declared in multiple object files. auto protoTableConst = createProtocolTable(decl); - protoInfo->protoTable = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); - protoInfo->protoTable->setInitializer(protoTableConst); + protoInfo->table = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA); + protoInfo->table->setInitializer(protoTableConst); protoInfo->ref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST); - protoInfo->ref->setInitializer(protoInfo->protoTable); + protoInfo->ref->setInitializer(protoInfo->table); - this->retain(protoInfo->protoTable); + this->retain(protoInfo->table); this->retain(protoInfo->ref); - return &protocols[decl]; + return protoInfo; } LLConstant *ObjCState::createProtocolList(ClassDeclaration *decl) { @@ -699,7 +700,7 @@ LLConstant *ObjCState::createProtocolList(ClassDeclaration *decl) { // TODO: throw an error if you try to include a non-objective-c interface? if (ifacesym->classKind == ClassKind::objc) { if (auto proto = getProtocol(ifacesym)) { - protoList.push_back(proto->protoTable); + protoList.push_back(proto->table); } } } @@ -726,10 +727,10 @@ ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) { auto methodInfo = &methods[decl]; auto name = objcResolveName(decl); + auto type = objcGetTypeEncoding(decl->type); methodInfo->name = makeGlobalStr(name, "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME); - methodInfo->type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); + methodInfo->type = makeGlobalStr(type, "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE); methodInfo->selector = makeGlobalRef(methodInfo->name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true); - methodInfo->llfunction = decl->fbody ? DtoBitCast(DtoCallee(decl), getOpaquePtrType()) : getNullPtr(); @@ -737,6 +738,7 @@ ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) { this->retain(methodInfo->name); this->retain(methodInfo->type); this->retain(methodInfo->selector); + return &methods[decl]; } @@ -748,12 +750,15 @@ LLConstant *ObjCState::createMethodInfo(FuncDeclaration *decl) { ); } -LLConstant *ObjCState::createMethodList(ClassDeclaration *decl, bool meta, bool optional) { - auto methodDeclList = getMethodsForType(meta ? decl : decl->objc.metaclass, optional); - +LLConstant *ObjCState::createMethodList(ClassDeclaration *decl, bool optional) { LLConstantList methodList; - for(auto func : methodDeclList) { - methodList.push_back(createMethodInfo(func)); + + if (decl) { + + auto methodDeclList = getMethodsForType(decl, optional); + for(auto func : methodDeclList) { + methodList.push_back(createMethodInfo(func)); + } } return objcEmitList(module, methodList, false); } @@ -859,12 +864,19 @@ LLValue *ObjCState::deref(ClassDeclaration *decl, LLType *as) { ObjcList ObjCState::getMethodsForType(ClassDeclaration *decl, bool optional) { ObjcList funcs; + bool isProtocol = decl->isInterfaceDeclaration(); if (decl) { for(size_t i = 0; i < decl->objc.methodList.length; i++) { auto method = decl->objc.methodList.ptr[i]; - if (method->objc.isOptional == optional) + if (isProtocol) { + if (method->objc.isOptional == optional) + funcs.push_back(method); + continue; + } + + if (method->fbody) funcs.push_back(method); } } diff --git a/gen/objcgen.h b/gen/objcgen.h index 01718c2a40d..5e19d7f38fa 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -121,8 +121,7 @@ struct ObjcClassInfo { LLGlobalVariable *ref; LLGlobalVariable *name; - LLGlobalVariable *classTable; - LLGlobalVariable *metaclassTable; + LLGlobalVariable *table; }; struct ObjcProtocolInfo { @@ -130,7 +129,7 @@ struct ObjcProtocolInfo { LLGlobalVariable *ref; LLGlobalVariable *name; - LLGlobalVariable *protoTable; + LLGlobalVariable *table; }; struct ObjcMethodInfo { @@ -178,7 +177,7 @@ class ObjCState { // used in method lists. ObjcMap methods; LLConstant *createMethodInfo(FuncDeclaration *decl); - LLConstant *createMethodList(ClassDeclaration *decl, bool meta, bool optional); + LLConstant *createMethodList(ClassDeclaration *decl, bool optional); // class_t and class_ro_t generation. ObjcMap classes; @@ -187,6 +186,14 @@ class ObjCState { LLConstant *getClassRoTable(ClassDeclaration *decl); LLConstant *getClassTable(ClassDeclaration *decl); + // Class names and refs need to be replicated + // for RO structs, as such we store + // then seperately. + llvm::StringMap classNames; + llvm::StringMap classRefs; + LLConstant *getClassName(ClassDeclaration *decl); + LLConstant *getClassRef(ClassDeclaration *decl); + // protocol_t generation. ObjcMap protocols; LLConstant *createProtocolTable(InterfaceDeclaration *decl); From 7423d0a217efe6605bd385f0e0d35d4e2ea9f069 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 13:42:01 +0100 Subject: [PATCH 64/67] Fix super context calls --- gen/objcgen.cpp | 4 ++-- gen/objcgen.h | 4 ++-- gen/tocall.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 6c43a2e1f66..9e9fe28bfb5 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -457,7 +457,7 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) { LLGlobalVariable *protocolList = nullptr; LLGlobalVariable *methodList = nullptr; - if (auto baseMethods = createMethodList(decl, false)) { + if (auto baseMethods = createMethodList(decl)) { methodList = getOrCreate(objcGetClassMethodListSymbol(name, meta), baseMethods->getType(), OBJC_SECNAME_CONST); methodList->setInitializer(baseMethods); } @@ -569,7 +569,7 @@ LLConstant *ObjCState::getClassRef(ClassDeclaration *decl) { return it->second; } - auto retval = getOrCreate("OBJC_CLASSLIST_REFERENCES_$_", getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); + auto retval = makeGlobal("OBJC_CLASSLIST_REFERENCES_$_", getOpaquePtrType(), OBJC_SECNAME_CLASSREFS); classRefs[className] = retval; this->retain(retval); return retval; diff --git a/gen/objcgen.h b/gen/objcgen.h index 5e19d7f38fa..c5c12573883 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -177,7 +177,7 @@ class ObjCState { // used in method lists. ObjcMap methods; LLConstant *createMethodInfo(FuncDeclaration *decl); - LLConstant *createMethodList(ClassDeclaration *decl, bool optional); + LLConstant *createMethodList(ClassDeclaration *decl, bool optional = false); // class_t and class_ro_t generation. ObjcMap classes; @@ -200,7 +200,7 @@ class ObjCState { LLConstant *createProtocolList(ClassDeclaration *decl); // Private helpers - ObjcList getMethodsForType(ClassDeclaration *decl, bool optional); + ObjcList getMethodsForType(ClassDeclaration *decl, bool optional = false); ObjcList retainedSymbols; void retain(LLConstant *symbol); diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 6e2a9cceeeb..ba72b6908e4 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -738,16 +738,17 @@ class ImplicitArgumentsBuilder { if (objccall && directcall) { // ... or a Objective-c direct call argument - if (auto parentfd = dfnval->func->isFuncDeclaration()) { - if (auto cls = parentfd->parent->isClassDeclaration()) { + if (auto func = dfnval->func->isFuncDeclaration()) { + if (auto klass = func->isThis()->isClassDeclaration()) { // Create obj_super struct with (this, ) auto obj_super = DtoAggrPair( DtoBitCast(dfnval->vthis, argtype), - gIR->objc.deref(cls, getOpaquePtrType()), + gIR->objc.deref(klass, getOpaquePtrType()), "super" ); + // Allocate and store obj_super struct into a new variable. auto clsaddr = DtoRawAlloca(obj_super->getType(), 16, "super"); DtoStore(obj_super, clsaddr); From 5d86126317e5a61618f10e7580b3d0278313a9b9 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 17:53:39 +0100 Subject: [PATCH 65/67] Move -L-w flag to d_do_test and use IN_LLVM in objc.d/h --- dmd/objc.d | 2 +- dmd/objc.h | 2 ++ tests/dmd/runnable/extra-files/test16096a.d | 13 ++++++++----- tests/dmd/runnable/objc_call.d | 2 +- tests/dmd/runnable/objc_call_static.d | 2 +- tests/dmd/runnable/objc_class.d | 2 +- tests/dmd/runnable/objc_external_class_19700.d | 2 +- tests/dmd/runnable/objc_instance_variable.d | 2 +- tests/dmd/runnable/objc_objc_msgSend.d | 2 +- tests/dmd/runnable/objc_protocol.d | 2 +- tests/dmd/runnable/objc_protocol_sections.d | 2 +- tests/dmd/runnable/objc_self_test.d | 2 +- tests/dmd/runnable/objc_super_call.d | 2 +- tests/dmd/tools/d_do_test.d | 7 +++++++ 14 files changed, 28 insertions(+), 16 deletions(-) diff --git a/dmd/objc.d b/dmd/objc.d index fcc80029850..b5f7ea39614 100644 --- a/dmd/objc.d +++ b/dmd/objc.d @@ -157,7 +157,7 @@ extern (C++) struct ObjcClassDeclaration bool isExtern = false; /// `true` if this class is a Swift stub - bool isSwiftStub = false; + version(IN_LLVM) bool isSwiftStub = false; /// Name of this class. Identifier identifier; diff --git a/dmd/objc.h b/dmd/objc.h index 7ad1a174ee4..03b72dec4e3 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -37,7 +37,9 @@ struct ObjcClassDeclaration { d_bool isMeta; d_bool isExtern; +#if IN_LLVM d_bool isSwiftStub; +#endif Identifier* identifier; ClassDeclaration* classDeclaration; diff --git a/tests/dmd/runnable/extra-files/test16096a.d b/tests/dmd/runnable/extra-files/test16096a.d index 4be18c738e4..b6890ec5030 100644 --- a/tests/dmd/runnable/extra-files/test16096a.d +++ b/tests/dmd/runnable/extra-files/test16096a.d @@ -3,19 +3,22 @@ module test16096a; import core.attribute : selector; extern (Objective-C) -interface Class { +interface Class +{ NSObject alloc() @selector("alloc"); } extern (Objective-C) -interface NSObject { - NSObject initWithUTF8String(const(char)* str) @selector("initWithUTF8String:"); +interface NSObject +{ + NSObject initWithUTF8String(in char* str) @selector("initWithUTF8String:"); void release() @selector("release"); } -extern (C) Class objc_lookUpClass(const(char)* name); +extern (C) Class objc_lookUpClass(in char* name); -void test() { +void test() +{ auto c = objc_lookUpClass("NSString"); auto o = c.alloc().initWithUTF8String("hello"); o.release(); diff --git a/tests/dmd/runnable/objc_call.d b/tests/dmd/runnable/objc_call.d index 9c8335ef89a..98199a0abb4 100644 --- a/tests/dmd/runnable/objc_call.d +++ b/tests/dmd/runnable/objc_call.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_call_static.d b/tests/dmd/runnable/objc_call_static.d index e8a53c6b41d..2c51413cc6b 100644 --- a/tests/dmd/runnable/objc_call_static.d +++ b/tests/dmd/runnable/objc_call_static.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_class.d b/tests/dmd/runnable/objc_class.d index 84a930cef6a..e66528ee6a9 100644 --- a/tests/dmd/runnable/objc_class.d +++ b/tests/dmd/runnable/objc_class.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_class.m -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_external_class_19700.d b/tests/dmd/runnable/objc_external_class_19700.d index 3e3732a624a..074cb2cc05b 100644 --- a/tests/dmd/runnable/objc_external_class_19700.d +++ b/tests/dmd/runnable/objc_external_class_19700.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_instance_variable.m -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_instance_variable.d b/tests/dmd/runnable/objc_instance_variable.d index fbbe4600013..9e2ce1075cb 100644 --- a/tests/dmd/runnable/objc_instance_variable.d +++ b/tests/dmd/runnable/objc_instance_variable.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_instance_variable.m -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_objc_msgSend.d b/tests/dmd/runnable/objc_objc_msgSend.d index 5b364d74baa..32f8574f17f 100644 --- a/tests/dmd/runnable/objc_objc_msgSend.d +++ b/tests/dmd/runnable/objc_objc_msgSend.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_objc_msgSend.m -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_protocol.d b/tests/dmd/runnable/objc_protocol.d index c6cd7d80d64..3cac51e1c97 100644 --- a/tests/dmd/runnable/objc_protocol.d +++ b/tests/dmd/runnable/objc_protocol.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_protocol.m -// REQUIRED_ARGS: -L-lobjc -L-w +// REQUIRED_ARGS: -L-lobjc import core.attribute : selector; diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index 0276843f70f..0f4a669f2d4 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: -// REQUIRED_ARGS: -L-lobjc -L-w +// REQUIRED_ARGS: -L-lobjc // This file verifies that Objective-C protocols are put in the correct segments // and sections in the binary. If not, functions from the Objective-C runtime diff --git a/tests/dmd/runnable/objc_self_test.d b/tests/dmd/runnable/objc_self_test.d index 8747ec091f8..407d80faed2 100644 --- a/tests/dmd/runnable/objc_self_test.d +++ b/tests/dmd/runnable/objc_self_test.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_self_test.m -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation extern (C) int getValue(); diff --git a/tests/dmd/runnable/objc_super_call.d b/tests/dmd/runnable/objc_super_call.d index 892c8e0760c..f0926cb2403 100644 --- a/tests/dmd/runnable/objc_super_call.d +++ b/tests/dmd/runnable/objc_super_call.d @@ -1,5 +1,5 @@ // EXTRA_OBJC_SOURCES: objc_super_call.m -// REQUIRED_ARGS: -L-framework -LFoundation -L-w +// REQUIRED_ARGS: -L-framework -LFoundation import core.attribute : selector; diff --git a/tests/dmd/tools/d_do_test.d b/tests/dmd/tools/d_do_test.d index d362e76c820..965147926ae 100755 --- a/tests/dmd/tools/d_do_test.d +++ b/tests/dmd/tools/d_do_test.d @@ -816,6 +816,13 @@ bool gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_ testArgs.objcSources = split(extraObjcSourcesStr); + // Using the ld64 linker may result in linker warnings being generated + // which are irrelevant to the parts of Objective-C ABI which D implements. + // As such we supress linker warnings for Objective-C tests to avoid + // linker errors when linking against Objective-C compiler output. + if (objc && testArgs.objcSources.length > 0) + testArgs.requiredArgs ~= " -L-w"; + // swap / with $SEP if (envData.sep && envData.sep != "/") foreach (ref s; testArgs.sources) From fd35f832c883a22f50e4d1c15bfcafcbe9060e99 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Thu, 28 Nov 2024 17:57:16 +0100 Subject: [PATCH 66/67] add LDC version tag to -L-w flag --- tests/dmd/tools/d_do_test.d | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/dmd/tools/d_do_test.d b/tests/dmd/tools/d_do_test.d index 965147926ae..4de96f38b3a 100755 --- a/tests/dmd/tools/d_do_test.d +++ b/tests/dmd/tools/d_do_test.d @@ -820,8 +820,9 @@ bool gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_ // which are irrelevant to the parts of Objective-C ABI which D implements. // As such we supress linker warnings for Objective-C tests to avoid // linker errors when linking against Objective-C compiler output. - if (objc && testArgs.objcSources.length > 0) - testArgs.requiredArgs ~= " -L-w"; + version(LDC) + if (objc) + testArgs.requiredArgs ~= " -L-w"; // swap / with $SEP if (envData.sep && envData.sep != "/") From 92448741bfd25b42d5eb488c98209c09cb7b1bf8 Mon Sep 17 00:00:00 2001 From: LunaTheFoxgirl Date: Fri, 29 Nov 2024 14:29:37 +0100 Subject: [PATCH 67/67] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e090682c4ab..07d3e391d53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - ldc2.conf: %%ldcconfigpath%% placeholder added - specifies the directory where current configuration file is located. (#4717) - Add support for building against a system copy of zlib through `-DPHOBOS_SYSTEM_ZLIB=ON`. (#4742) - Emscripten: The compiler now mimicks a musl Linux platform wrt. extra predefined versions (`linux`, `Posix`, `CRuntime_Musl`, `CppRuntime_LLVM`). (#4750) +- Objective-C: The compiler now properly supports Objective-C classes and protocols, as well as swift stub classes (via the `@swift` UDA). (#4777) #### Platform support