From 168445d6b1ef629dcbb3de69147ac47b8c526c26 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 24 Mar 2016 13:26:58 -0400 Subject: [PATCH 1/5] update dsymutil detection logic fix #15594 --- Make.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Make.inc b/Make.inc index 6d8ca1580a49f..ecadaca088d89 100644 --- a/Make.inc +++ b/Make.inc @@ -781,7 +781,9 @@ ifeq ($(OS), Darwin) INSTALL_NAME_ID_DIR := @rpath/ INSTALL_NAME_CMD := install_name_tool -id $(INSTALL_NAME_ID_DIR) INSTALL_NAME_CHANGE_CMD := install_name_tool -change -ifeq ($(shell test `dsymutil -v | cut -d\- -f2 | cut -d. -f1` -gt 102 && echo yes), yes) +ifneq (,$(findstring LLVM,$(shell dsymutil --version))) + DSYMUTIL := dsymutil +else ifeq ($(shell test `dsymutil -v | cut -d\- -f2 | cut -d. -f1` -gt 102 && echo yes), yes) DSYMUTIL := dsymutil else DSYMUTIL := true -ignore From 412367564053ce8f2cb3718f4d44a5c2df0ec147 Mon Sep 17 00:00:00 2001 From: Tony Kelman Date: Fri, 15 Jan 2016 14:15:01 -0800 Subject: [PATCH 2/5] Add test for and close #9222, segfault with nested functions --- test/inference.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/inference.jl b/test/inference.jl index c3afe6ffd9223..2c792f0019f7f 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -225,3 +225,23 @@ f11366{T}(x::Type{Ref{T}}) = Ref{x} let f(T) = Type{T} @test Base.return_types(f, Tuple{Type{Int}}) == [Type{Type{Int}}] end + +# issue #9222 +function SimpleTest9222{T1<:Real}(pdedata, mu_actual::Vector{T1}, + nu_actual::Vector{T1}, v0::Vector{T1}, epsilon::T1, beta::Vector{T1}, + delta::T1, l::T1, R::T1, s0::T1, show_trace::Bool = true) + return 0.0 +end +function SimpleTest9222{T1<:Real}(pdedata, mu_actual::Vector{T1}, + nu_actual::Vector{T1}, v0::Vector{T1}, epsilon::T1, beta::Vector{T1}, + delta::T1, l::T1, R::T1) + return SimpleTest9222(pdedata, mu_actual, nu_actual, v0, epsilon, + beta, delta, l, R, v0[1]) +end +function foo9222() + v0 = rand(10) + mu_actual = rand(10) + nu_actual = rand(10) + SimpleTest9222(0.0, mu_actual, nu_actual, v0, 0.0, [1.0,1.0], 0.5, 5.0, 20.0) +end +@test 0.0 == foo9222() From f2c699bf405b338719c666c715ef46f7cdbb9c6f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 25 Mar 2016 16:20:30 -0400 Subject: [PATCH 3/5] jl_get_llvmf requires manual execution of the PM now without this, code_llvm was showing un-optimized functions which could be very misleading since it differed from the code that actually executed (this was a recent regression caused by jn/moduledecoalescing) --- src/codegen.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index f20b25fae0637..4fca1cf311dbf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -206,13 +206,11 @@ static Module *shadow_output; #define jl_builderModule builder.GetInsertBlock()->getParent()->getParent() static MDBuilder *mbuilder; static std::map argNumberStrings; -#ifndef USE_ORCJIT #ifdef LLVM38 static legacy::PassManager *PM; #else static PassManager *PM; #endif -#endif #ifdef LLVM37 // No DataLayout pass needed anymore. @@ -1193,6 +1191,8 @@ void *jl_get_llvmf(jl_function_t *f, jl_tupletype_t *tt, bool getwrapper, bool g Function *f, *specf; jl_llvm_functions_t declarations; std::unique_ptr m = emit_function(linfo, &declarations); + finalize_gc_frame(m.get()); + PM->run(*m.get()); f = (llvm::Function*)declarations.functionObject; specf = (llvm::Function*)declarations.specFunctionObject; // swap declarations for definitions and destroy declarations @@ -1214,7 +1214,7 @@ void *jl_get_llvmf(jl_function_t *f, jl_tupletype_t *tt, bool getwrapper, bool g if (f_decl) { f->setName(f_decl->getName()); } - finalize_gc_frame(m.release()); // the return object `llvmf` will be the owning pointer + m.release(); // the return object `llvmf` will be the owning pointer JL_GC_POP(); if (getwrapper || !specf) { return f; @@ -5631,7 +5631,6 @@ static void init_julia_llvm_env(Module *m) jl_data_layout = new DataLayout(*jl_ExecutionEngine->getDataLayout()); #endif -#ifndef USE_ORCJIT #ifdef LLVM38 PM = new legacy::PassManager(); #else @@ -5646,7 +5645,6 @@ static void init_julia_llvm_env(Module *m) PM->add(jl_data_layout); #endif addOptimizationPasses(PM); -#endif } // Helper to figure out what features to set for the LLVM target From 3f4ba521ec488718d78afa20e8c9c8226f7f9219 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 25 Mar 2016 21:54:17 -0400 Subject: [PATCH 4/5] accelerate symbol lookup in OrcJIT CompileLayer.findSymbol is O(n) in the number of modules that have been emitted but we can pre-compute the result of findSymbolIn when notified that an object has been emitted and store it in one hash table for the JIT fix #15619 --- src/codegen.cpp | 10 +- src/codegen_internal.h | 4 - src/debuginfo.cpp | 25 +---- src/jitlayers.cpp | 224 ++++++++++++++++++++++------------------- 4 files changed, 126 insertions(+), 137 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4fca1cf311dbf..6453e95f13092 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1393,9 +1393,7 @@ const jl_value_t *jl_dump_function_asm(void *f, int raw_mc) #endif const object::ObjectFile *object = NULL; assert(fptr != 0); - bool isJIT = true; if (!jl_DI_for_fptr(fptr, &symsize, &slide, §ion_slide, &object, &context)) { - isJIT = false; if (!jl_dylib_DI_for_fptr(fptr, &object, &objcontext, &slide, §ion_slide, false, NULL, NULL, NULL, NULL)) { jl_printf(JL_STDERR, "WARNING: Unable to find function pointer\n"); @@ -1410,9 +1408,6 @@ const jl_value_t *jl_dump_function_asm(void *f, int raw_mc) } if (raw_mc) { -#ifdef LLVM37 - jl_cleanup_DI(context); -#endif return (jl_value_t*)jl_pchar_to_array((char*)fptr, symsize); } @@ -1428,10 +1423,7 @@ const jl_value_t *jl_dump_function_asm(void *f, int raw_mc) #endif ); -#ifdef LLVM37 - if (isJIT) - jl_cleanup_DI(context); -#else +#ifndef LLVM37 fstream.flush(); #endif diff --git a/src/codegen_internal.h b/src/codegen_internal.h index 860193849e59c..f939edc118807 100644 --- a/src/codegen_internal.h +++ b/src/codegen_internal.h @@ -27,10 +27,6 @@ extern int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide, int6 extern bool jl_dylib_DI_for_fptr(size_t pointer, const object::ObjectFile **object, llvm::DIContext **context, int64_t *slide, int64_t *section_slide, bool onlySysImg, bool *isSysImg, void **saddr, char **name, char **filename); -#ifdef USE_MCJIT -extern void jl_cleanup_DI(llvm::DIContext *context); -#endif - #ifdef USE_ORCJIT extern JL_DLLEXPORT void ORCNotifyObjectEmitted(JITEventListener *Listener, const object::ObjectFile &obj, diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 531cec7c56e4a..9c57c2b7116a0 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -91,10 +91,8 @@ struct ObjectInfo { const object::ObjectFile *object; size_t SectionSize; ptrdiff_t slide; -#ifdef LLVM39 +#ifdef LLVM37 DIContext *context; -#elif defined(LLVM37) - const llvm::LoadedObjectInfo *L; #endif #if defined(_OS_DARWIN_) && !defined(LLVM37) const char *name; @@ -444,11 +442,7 @@ class JuliaJITEventListener: public JITEventListener ObjectInfo tmp = {&debugObj, (size_t)SectionSize, (ptrdiff_t)(SectionAddr - SectionLoadAddr), -#ifdef LLVM39 new DWARFContextInMemory(debugObj, &L), -#else - L.clone().release(), -#endif }; objectmap[SectionLoadAddr] = tmp; first = false; @@ -524,11 +518,6 @@ class JuliaJITEventListener: public JITEventListener #endif ObjectInfo tmp = {objfile, (size_t)Size, (ptrdiff_t)(SectionAddr - SectionLoadAddr), -#ifdef LLVM39 - new DWARFContextInMemory(*objfile, &L), -#elif defined(LLVM37) - L.clone().release(), -#endif #ifdef _OS_DARWIN_ strndup(sName.data(), sName.size()), #endif @@ -1066,10 +1055,8 @@ int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide, int64_t *se *section_slide = fit->second.slide; *object = fit->second.object; if (context) { -#if defined(LLVM39) +#if defined(LLVM37) *context = fit->second.context; -#elif defined(LLVM37) - *context = new DWARFContextInMemory(*fit->second.object, fit->second.L); #else *context = DIContext::getDWARFContext(*fit->second.object); #endif @@ -1121,13 +1108,6 @@ JL_DLLEXPORT uint64_t jl_get_section_start(uint64_t fptr) #endif -void jl_cleanup_DI(llvm::DIContext *context) -{ -#ifndef LLVM39 - delete context; -#endif -} - // Set *name and *filename to either NULL or malloc'd string void jl_getFunctionInfo(char **name, char **filename, size_t *line, char **inlinedat_file, size_t *inlinedat_line, jl_lambda_info_t **outer_linfo, @@ -1151,7 +1131,6 @@ void jl_getFunctionInfo(char **name, char **filename, size_t *line, if (jl_DI_for_fptr(pointer, &symsize, &slide, NULL, &object, &context)) { *outer_linfo = jl_jit_events->lookupLinfo(pointer); lookup_pointer(context, name, line, filename, inlinedat_line, inlinedat_file, pointer+slide, 1, fromC); - jl_cleanup_DI(context); return; } #else // !USE_MCJIT diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index ce1679fd93b9e..b017a4bbf13d6 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -160,14 +160,6 @@ extern "C" { LLVM_ATTRIBUTE_NOINLINE extern void __jit_debug_register_code(); } -#if defined(_OS_DARWIN_) && defined(LLVM37) && defined(LLVM_SHLIB) -#define CUSTOM_MEMORY_MANAGER createRTDyldMemoryManagerOSX -extern RTDyldMemoryManager *createRTDyldMemoryManagerOSX(); -#elif defined(_OS_LINUX_) && defined(LLVM37) && defined(JL_UNW_HAS_FORMAT_IP) -#define CUSTOM_MEMORY_MANAGER createRTDyldMemoryManagerUnix -extern RTDyldMemoryManager *createRTDyldMemoryManagerUnix(); -#endif - namespace { using namespace llvm; @@ -190,109 +182,127 @@ void NotifyDebugger(jit_code_entry *JITCodeEntry) __jit_debug_descriptor.relevant_entry = JITCodeEntry; __jit_debug_register_code(); } +} // ------------------------ END OF TEMPORARY COPY FROM LLVM ----------------- -// Custom object emission notification handler for the JuliaOJIT -// TODO: hook up RegisterJITEventListener, instead of hard-coding the GDB and JuliaListener targets -class DebugObjectRegistrar { -private: - void NotifyGDB(OwningBinary &DebugObj) - { - const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart(); - size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize(); - - assert(Buffer && "Attempt to register a null object with a debugger."); - jit_code_entry *JITCodeEntry = new jit_code_entry(); - - if (!JITCodeEntry) { - jl_printf(JL_STDERR, "WARNING: Allocation failed when registering a JIT entry!\n"); - } - else { - JITCodeEntry->symfile_addr = Buffer; - JITCodeEntry->symfile_size = Size; - - NotifyDebugger(JITCodeEntry); - } - } - - std::vector> SavedObjects; - std::unique_ptr JuliaListener; - -public: - DebugObjectRegistrar() : JuliaListener(CreateJuliaJITEventListener()) {} +#if defined(_OS_DARWIN_) && defined(LLVM37) && defined(LLVM_SHLIB) +#define CUSTOM_MEMORY_MANAGER createRTDyldMemoryManagerOSX +extern RTDyldMemoryManager *createRTDyldMemoryManagerOSX(); +#elif defined(_OS_LINUX_) && defined(LLVM37) && defined(JL_UNW_HAS_FORMAT_IP) +#define CUSTOM_MEMORY_MANAGER createRTDyldMemoryManagerUnix +extern RTDyldMemoryManager *createRTDyldMemoryManagerUnix(); +#endif +#ifndef CUSTOM_MEMORY_MANAGER +#define CUSTOM_MEMORY_MANAGER() new SectionMemoryManager +#endif - template - void operator()(ObjectLinkingLayerBase::ObjSetHandleT, const ObjSetT &Objects, - const LoadResult &LOS) - { - auto oit = Objects.begin(); - auto lit = LOS.begin(); - while (oit != Objects.end()) { +// A simplified model of the LLVM ExecutionEngine that implements only the methods that Julia needs +// but tries to roughly match the API anyways so that compatibility is easier +class JuliaOJIT { + // Custom object emission notification handler for the JuliaOJIT + // TODO: hook up RegisterJITEventListener, instead of hard-coding the GDB and JuliaListener targets + class DebugObjectRegistrar { + public: + DebugObjectRegistrar(JuliaOJIT &JIT) + : JuliaListener(CreateJuliaJITEventListener()), + JIT(JIT) {} + + template + void operator()(ObjectLinkingLayerBase::ObjSetHandleT H, const ObjSetT &Objects, + const LoadResult &LOS) + { + auto oit = Objects.begin(); + auto lit = LOS.begin(); + for (; oit != Objects.end(); ++oit, ++lit) { #ifdef LLVM39 - const auto &Object = (*oit)->getBinary(); + const auto &Object = (*oit)->getBinary(); #else - auto &Object = *oit; + auto &Object = *oit; #endif - auto &LO = *lit; - - OwningBinary SavedObject = LO->getObjectForDebug(*Object); + auto &LO = *lit; + + OwningBinary SavedObject = LO->getObjectForDebug(*Object); + + // If the debug object is unavailable, save (a copy of) the original object + // for our backtraces + if (!SavedObject.getBinary()) { + // This is unfortunate, but there doesn't seem to be a way to take + // ownership of the original buffer + auto NewBuffer = MemoryBuffer::getMemBufferCopy(Object->getData(), Object->getFileName()); + auto NewObj = ObjectFile::createObjectFile(NewBuffer->getMemBufferRef()); + SavedObject = OwningBinary(std::move(*NewObj),std::move(NewBuffer)); + } + else { + NotifyGDB(SavedObject); + } - // If the debug object is unavailable, save (a copy of) the original object - // for our backtraces - if (!SavedObject.getBinary()) { - // This is unfortunate, but there doesn't seem to be a way to take - // ownership of the original buffer - auto NewBuffer = MemoryBuffer::getMemBufferCopy(Object->getData(), Object->getFileName()); - auto NewObj = ObjectFile::createObjectFile(NewBuffer->getMemBufferRef()); - SavedObject = OwningBinary(std::move(*NewObj),std::move(NewBuffer)); - } - else { - NotifyGDB(SavedObject); + SavedObjects.push_back(std::move(SavedObject)); + + ORCNotifyObjectEmitted(JuliaListener.get(), + *Object, + *SavedObjects.back().getBinary(), + *LO); + + // record all of the exported symbols defined in this object + // in the primary hash table for the enclosing JIT + for (auto &Symbol : Object->symbols()) { + auto Flags = Symbol.getFlags(); + if (Flags & object::BasicSymbolRef::SF_Undefined) + continue; + if (!(Flags & object::BasicSymbolRef::SF_Exported)) + continue; + ErrorOr Name = Symbol.getName(); + orc::JITSymbol Sym = JIT.CompileLayer.findSymbolIn(H, *Name, true); + assert(Sym); + // note: calling getAddress here eagerly finalizes H + // as an alternative, we could store the JITSymbol instead + // (which would present a lazy-initializer functor interface instead) + JIT.LocalSymbolTable[*Name] = (void*)(uintptr_t)Sym.getAddress(); + } } + } - SavedObjects.push_back(std::move(SavedObject)); + private: + void NotifyGDB(OwningBinary &DebugObj) + { + const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart(); + size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize(); - ORCNotifyObjectEmitted(JuliaListener.get(), - *Object, - *SavedObjects.back().getBinary(), - *LO); + assert(Buffer && "Attempt to register a null object with a debugger."); + jit_code_entry *JITCodeEntry = new jit_code_entry(); - ++oit; - ++lit; + if (!JITCodeEntry) { + jl_printf(JL_STDERR, "WARNING: Allocation failed when registering a JIT entry!\n"); + } + else { + JITCodeEntry->symfile_addr = Buffer; + JITCodeEntry->symfile_size = Size; + + NotifyDebugger(JITCodeEntry); + } } - } -}; -// A simplified model of the LLVM ExecutionEngine that implements only the methods that Julia needs -// but tries to roughly match the API anyways so that compatibility is easier -class JuliaOJIT { + std::vector> SavedObjects; + std::unique_ptr JuliaListener; + JuliaOJIT &JIT; + }; + public: typedef orc::ObjectLinkingLayer ObjLayerT; typedef orc::IRCompileLayer CompileLayerT; typedef CompileLayerT::ModuleSetHandleT ModuleHandleT; - typedef StringMap GlobalSymbolTableT; + typedef StringMap SymbolTableT; typedef object::OwningBinary OwningObj; JuliaOJIT(TargetMachine &TM) : TM(TM), DL(TM.createDataLayout()), ObjStream(ObjBufferSV), - MemMgr( -#ifdef CUSTOM_MEMORY_MANAGER - CUSTOM_MEMORY_MANAGER() -#else - new SectionMemoryManager -#endif - ) { -#ifdef JL_DEBUG_BUILD - PM.add(createVerifierPass()); -#endif - addOptimizationPasses(&PM); - if (TM.addPassesToEmitMC(PM, Ctx, ObjStream)) - llvm_unreachable("Target does not support MC emission."); - - CompileLayer = std::unique_ptr{new CompileLayerT(ObjectLayer, - [&](Module &M) { + MemMgr(CUSTOM_MEMORY_MANAGER()), + ObjectLayer(DebugObjectRegistrar(*this)), + CompileLayer( + ObjectLayer, + [this](Module &M) { PM.run(M); std::unique_ptr ObjBuffer( new ObjectMemoryBuffer(std::move(ObjBufferSV))); @@ -307,11 +317,18 @@ class JuliaOJIT { return OwningObj(std::move(*Obj), std::move(ObjBuffer)); } - )}; + ) + { +#ifdef JL_DEBUG_BUILD + PM.add(createVerifierPass()); +#endif + addOptimizationPasses(&PM); + if (TM.addPassesToEmitMC(PM, Ctx, ObjStream)) + llvm_unreachable("Target does not support MC emission."); + // Make sure SectionMemoryManager::getSymbolAddressInProcess can resolve // symbols in the program as well. The nullptr argument to the function // tells DynamicLibrary to load the program, not a library. - std::string *ErrorStr = nullptr; if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, ErrorStr)) report_fatal_error("FATAL: unable to dlopen self\n" + *ErrorStr); @@ -336,8 +353,9 @@ class JuliaOJIT { assert(successful); } - void *getPointerToGlobalIfAvailable(StringRef S) { - GlobalSymbolTableT::const_iterator pos = GlobalSymbolTable.find(S); + void *getPointerToGlobalIfAvailable(StringRef S) + { + SymbolTableT::const_iterator pos = GlobalSymbolTable.find(S); if (pos != GlobalSymbolTable.end()) return pos->second; return nullptr; @@ -379,23 +397,27 @@ class JuliaOJIT { ); SmallVector,1> Ms; Ms.push_back(std::move(M)); - return CompileLayer->addModuleSet(std::move(Ms), + return CompileLayer.addModuleSet(std::move(Ms), MemMgr, std::move(Resolver)); } - void removeModule(ModuleHandleT H) { CompileLayer->removeModuleSet(H); } + void removeModule(ModuleHandleT H) + { + CompileLayer.removeModuleSet(H); + } orc::JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { + void *Addr = nullptr; if (ExportedSymbolsOnly) { // Step 1: Check against list of known external globals - void *Addr = getPointerToGlobalIfAvailable(Name); - if (Addr != nullptr) - return orc::JITSymbol((uintptr_t)Addr, JITSymbolFlags::Exported); + Addr = getPointerToGlobalIfAvailable(Name); } // Step 2: Search all previously emitted symbols - return CompileLayer->findSymbol(Name, ExportedSymbolsOnly); + if (Addr == nullptr) + Addr = LocalSymbolTable[Name]; + return orc::JITSymbol((uintptr_t)Addr, JITSymbolFlags::Exported); } orc::JITSymbol findUnmangledSymbol(const std::string Name) @@ -444,11 +466,11 @@ class JuliaOJIT { MCContext *Ctx; RTDyldMemoryManager *MemMgr; ObjLayerT ObjectLayer; - std::unique_ptr CompileLayer; - GlobalSymbolTableT GlobalSymbolTable; + CompileLayerT CompileLayer; + SymbolTableT GlobalSymbolTable; + SymbolTableT LocalSymbolTable; }; -} #endif #ifdef USE_ORCJIT From 916b915998101beba006bf9dc7c620e6f28510ad Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 25 Mar 2016 22:44:15 -0400 Subject: [PATCH 5/5] add test for thousands of function ast constants in a module closes #14113 (fixed previously) --- test/core.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/core.jl b/test/core.jl index dca1c6d3b2aa6..56fdb27f561c0 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3726,3 +3726,18 @@ end @test foo1784() @test a[1] == true end + +# issue #14113 +module A14113 + using Base.Test + # show that making several thousand methods (and lots of AST constants) + # doesn't cause any serious issues (for example, for the serializer) + # although to keep runtime on the order of several seconds for this test, + # only several hundred of them are compiled / called + for i = 1:2^14 + 256 + r = rand(2^4) + code = Expr(:tuple, r...) + f = @eval () -> $code + i > (2^14 - 256) && @test [f()...] == r + end +end