From 95915a8ab8ef0accae9d423adb23e4fbd9b447bc Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 26 Oct 2017 20:24:03 +0200 Subject: [PATCH] Fix and refactor Objective-C state Tie the state to an LLVM module/object file instead of having a global one. And finalize it once per LLVM module instead of once per D module (previously, as part of ModuleInfo generation). Fixes issue #2388. --- ddmd/gluelayer.d | 2 +- driver/codegenerator.cpp | 2 + gen/irstate.cpp | 2 +- gen/irstate.h | 5 +- gen/moduleinfo.cpp | 3 - gen/objcgen.cpp | 175 ++++++++++++++------------------- gen/objcgen.h | 35 ++++++- gen/tocall.cpp | 3 +- tests/compilable/objc_gh2388.d | 15 +++ 9 files changed, 130 insertions(+), 112 deletions(-) create mode 100644 tests/compilable/objc_gh2388.d diff --git a/ddmd/gluelayer.d b/ddmd/gluelayer.d index 62deccb1cd..a4d6b0b329 100644 --- a/ddmd/gluelayer.d +++ b/ddmd/gluelayer.d @@ -32,7 +32,7 @@ version (IN_LLVM) { Statement asmSemantic(AsmStatement s, Scope* sc); RET retStyle(TypeFunction tf); - void objc_initSymbols(); // in gen/objcgen.cpp + void objc_initSymbols() {} } } else version (NoBackend) diff --git a/driver/codegenerator.cpp b/driver/codegenerator.cpp index e797e8ed7b..287bc74772 100644 --- a/driver/codegenerator.cpp +++ b/driver/codegenerator.cpp @@ -247,6 +247,8 @@ void CodeGenerator::finishLLModule(Module *m) { } void CodeGenerator::writeAndFreeLLModule(const char *filename) { + ir_->objc.finalize(); + // Issue #1829: make sure all replaced global variables are replaced // everywhere. ir_->replaceGlobals(); diff --git a/gen/irstate.cpp b/gen/irstate.cpp index 4b37a34907..6818aafc0a 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -35,7 +35,7 @@ IRScope &IRScope::operator=(const IRScope &rhs) { //////////////////////////////////////////////////////////////////////////////// IRState::IRState(const char *name, llvm::LLVMContext &context) - : module(name, context), DBuilder(this) { + : module(name, context), DBuilder(this), objc(module) { ir.state = this; } diff --git a/gen/irstate.h b/gen/irstate.h index 5f46c96265..c529457496 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -23,6 +23,7 @@ #include "aggregate.h" #include "root.h" #include "gen/dibuilder.h" +#include "gen/objcgen.h" #include "ir/iraggr.h" #include "ir/irvar.h" #include "llvm/ADT/StringMap.h" @@ -105,7 +106,7 @@ struct IRAsmBlock { retfixup(nullptr) {} }; -// represents the module +// represents the LLVM module (object file) struct IRState { private: std::vector> @@ -125,6 +126,8 @@ public: LLStructType *moduleRefType = nullptr; + ObjCState objc; + // Stack of currently codegen'd functions (more than one for lambdas or other // nested functions, inlining-only codegen'ing, etc.), and some convenience // accessors for the top-most one. diff --git a/gen/moduleinfo.cpp b/gen/moduleinfo.cpp index 2b888d8ea3..eb546c1ac2 100644 --- a/gen/moduleinfo.cpp +++ b/gen/moduleinfo.cpp @@ -15,7 +15,6 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/mangling.h" -#include "gen/objcgen.h" #include "gen/rttibuilder.h" #include "ir/irfunction.h" #include "ir/irmodule.h" @@ -306,8 +305,6 @@ llvm::GlobalVariable *genModuleInfo(Module *m) { const auto at = llvm::ArrayType::get(it, len); b.push(toConstantArray(it, at, name, len, false)); - objc_Module_genmoduleinfo_classes(); - // Create a global symbol with the above initialiser. LLGlobalVariable *moduleInfoSym = getIrModule(m)->moduleInfoSymbol(); b.finalize(moduleInfoSym->getType()->getPointerElementType(), moduleInfoSym); diff --git a/gen/objcgen.cpp b/gen/objcgen.cpp index 0679818eb7..abd68d8071 100644 --- a/gen/objcgen.cpp +++ b/gen/objcgen.cpp @@ -9,100 +9,27 @@ // //===----------------------------------------------------------------------===// +#include "gen/objcgen.h" #include "mtype.h" #include "objc.h" #include "gen/irstate.h" -#include "gen/objcgen.h" namespace { -// Were any Objective-C symbols generated? -bool hasSymbols; - -enum ABI { - none = 0, - fragile = 1, - nonFragile = 2 -}; - +enum ABI { none = 0, fragile = 1, nonFragile = 2 }; ABI abi = nonFragile; - -// symbols that shouldn't be optimized away -std::vector retainedSymbols; - -llvm::StringMap methVarNameMap; -llvm::StringMap methVarRefMap; - -void retain(LLConstant *sym) { - retainedSymbols.push_back(DtoBitCast(sym, getVoidPtrType())); } -void retainSymbols() { - // put all objc symbols in the llvm.compiler.used array so optimizer won't - // remove. Should do just once per module. - auto arrayType = LLArrayType::get(getVoidPtrType(), retainedSymbols.size()); - auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); - auto var = new LLGlobalVariable - (gIR->module, usedArray->getType(), false, - LLGlobalValue::AppendingLinkage, - usedArray, - "llvm.compiler.used"); - var->setSection("llvm.metadata"); -} - -void genImageInfo() { - // Use LLVM to generate image info - const char *section = (abi == nonFragile ? - "__DATA,__objc_imageinfo,regular,no_dead_strip" : - "__OBJC,__image_info"); - gIR->module.addModuleFlag(llvm::Module::Error, - "Objective-C Version", abi); // unused? - gIR->module.addModuleFlag(llvm::Module::Error, - "Objective-C Image Info Version", 0u); // version - gIR->module.addModuleFlag(llvm::Module::Error, - "Objective-C Image Info Section", - llvm::MDString::get(gIR->context(), section)); - gIR->module.addModuleFlag(llvm::Module::Override, - "Objective-C Garbage Collection", 0u); // flags -} - -LLGlobalVariable *getCStringVar(const char *symbol, - const llvm::StringRef &str, - const char *section) { - auto init = llvm::ConstantDataArray::getString(gIR->context(), str); - auto var = new LLGlobalVariable - (gIR->module, init->getType(), false, - LLGlobalValue::PrivateLinkage, init, symbol); - var->setSection(section); - return var; -} - -LLGlobalVariable *getMethVarName(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; -} -} // end local stuff - bool objc_isSupported(const llvm::Triple &triple) { if (triple.isOSDarwin()) { // Objective-C only supported on Darwin at this time 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 + 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 + case llvm::Triple::x86: // OSX, iOS, watchOS sim abi = fragile; return true; default: @@ -112,15 +39,32 @@ bool objc_isSupported(const llvm::Triple &triple) { return false; } -// called by the ddmd.objc.Supported ctor -void objc_initSymbols() { - hasSymbols = false; - retainedSymbols.clear(); - methVarNameMap.clear(); - methVarRefMap.clear(); +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; } -LLGlobalVariable *objc_getMethVarRef(const ObjcSelector &sel) { +LLGlobalVariable *ObjCState::getMethVarName(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; +} + +LLGlobalVariable *ObjCState::getMethVarRef(const ObjcSelector &sel) { llvm::StringRef s(sel.stringvalue, sel.stringlen); auto it = methVarRefMap.find(s); if (it != methVarRefMap.end()) { @@ -128,30 +72,59 @@ LLGlobalVariable *objc_getMethVarRef(const ObjcSelector &sel) { } auto gvar = getMethVarName(s); - auto selref = new LLGlobalVariable - (gIR->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"); + 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); - hasSymbols = true; return selref; } -void objc_Module_genmoduleinfo_classes() { - if (hasSymbols) { +void ObjCState::retain(LLConstant *sym) { + retainedSymbols.push_back(DtoBitCast(sym, getVoidPtrType())); +} + +void ObjCState::finalize() { + 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 +} + +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"); +} diff --git a/gen/objcgen.h b/gen/objcgen.h index a1795e8e41..ebc36141d2 100644 --- a/gen/objcgen.h +++ b/gen/objcgen.h @@ -14,15 +14,44 @@ #ifndef LDC_GEN_OBJCGEN_H #define LDC_GEN_OBJCGEN_H +#include +#include "llvm/ADT/StringMap.h" + struct ObjcSelector; namespace llvm { +class Constant; class GlobalVariable; +class Module; class Triple; } bool objc_isSupported(const llvm::Triple &triple); -void objc_initSymbols(); -void objc_Module_genmoduleinfo_classes(); -llvm::GlobalVariable *objc_getMethVarRef(const ObjcSelector &sel); + +// Objective-C state tied to an LLVM module (object file). +class ObjCState { +public: + ObjCState(llvm::Module &module) : module(module) {} + + llvm::GlobalVariable *getMethVarRef(const ObjcSelector &sel); + void finalize(); + +private: + llvm::Module &module; + + // symbols that shouldn't be optimized away + std::vector retainedSymbols; + + llvm::StringMap methVarNameMap; + llvm::StringMap methVarRefMap; + + llvm::GlobalVariable *getCStringVar(const char *symbol, + const llvm::StringRef &str, + const char *section); + llvm::GlobalVariable *getMethVarName(const llvm::StringRef &name); + void retain(llvm::Constant *sym); + + void genImageInfo(); + void retainSymbols(); +}; #endif // LDC_GEN_OBJCGEN_H diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 268eb5ec9b..1653036ca6 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -22,7 +22,6 @@ #include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/nested.h" -#include "gen/objcgen.h" #include "gen/tollvm.h" #include "gen/runtime.h" #include "ir/irfunction.h" @@ -774,7 +773,7 @@ private: assert(dfnval); const auto selector = dfnval->func->selector; assert(selector); - LLGlobalVariable *selptr = objc_getMethVarRef(*selector); + LLGlobalVariable *selptr = gIR->objc.getMethVarRef(*selector); args.push_back(DtoBitCast(DtoLoad(selptr), getVoidPtrType())); } } diff --git a/tests/compilable/objc_gh2388.d b/tests/compilable/objc_gh2388.d new file mode 100644 index 0000000000..72df686e3d --- /dev/null +++ b/tests/compilable/objc_gh2388.d @@ -0,0 +1,15 @@ +// REQUIRES: Darwin +// RUN: %ldc -c -singleobj %s %S/objc_gh2387.d + +void alloc() +{ + NSObject o; + o.alloc(); +} + +extern (Objective-C): + +interface NSObject +{ + NSObject alloc() @selector("alloc"); +}