Skip to content

Commit

Permalink
Switch aarch64-darwin codegen to JITLink (ObjectLinkingLayer) and sma…
Browse files Browse the repository at this point in the history
…ll code model

This fixes #41440, #43285 and similar issues, which stem from
CodeModel::Large not being correctly implemented on MachO/ARM64.

Requires LLVM 13.x or Git main (tested: 1dd5e6fed5db with patches
from the JuliaLang/llvm-project julia-release/13.x branch, available
at https://github.com/dnadlinger/llvm-project/commits/julia-main).

Requires an LLVM patch to pass through __eh_frame unwind information,
without which backtraces silently won't work (already applied on
JuliaLang/llvm-project@julia-release/13.x):
llvm/llvm-project#52921
  • Loading branch information
dnadlinger committed Jan 10, 2022
1 parent cd334b2 commit 955d427
Show file tree
Hide file tree
Showing 4 changed files with 401 additions and 129 deletions.
15 changes: 12 additions & 3 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8100,7 +8100,12 @@ extern "C" void jl_init_llvm(void)
}
// Allocate a target...
Optional<CodeModel::Model> codemodel =
#ifdef _P64
#if defined(JL_USE_JITLINK)
// JITLink can patch up relocations between far objects so we can use the
// small code model – which is good, as the large code model is unmaintained
// on MachO/AArch64.
CodeModel::Small;
#elif defined(_P64)
// Make sure we are using the large code model on 64bit
// Let LLVM pick a default suitable for jitting on 32bit
CodeModel::Large;
Expand Down Expand Up @@ -8142,13 +8147,15 @@ extern "C" void jl_init_llvm(void)
}
#endif
if (jl_using_gdb_jitevents)
jl_ExecutionEngine->RegisterJITEventListener(JITEventListener::createGDBRegistrationListener());
jl_ExecutionEngine->enableJITDebuggingSupport();

#if defined(JL_USE_INTEL_JITEVENTS) || \
defined(JL_USE_OPROFILE_JITEVENTS) || \
defined(JL_USE_PERF_JITEVENTS)
#ifdef JL_USE_JITLINK
#error "JIT profiling support (JL_USE_*_JITEVENTS) not yet available on platforms that use JITLink"
#else
const char *jit_profiling = getenv("ENABLE_JITPROFILING");
#endif

#if defined(JL_USE_INTEL_JITEVENTS)
if (jit_profiling && atoi(jit_profiling)) {
Expand Down Expand Up @@ -8181,6 +8188,8 @@ extern "C" void jl_init_llvm(void)
#ifdef JL_USE_PERF_JITEVENTS
if (jl_using_perf_jitevents)
jl_ExecutionEngine->RegisterJITEventListener(JITEventListener::createPerfJITEventListener());
#endif
#endif
#endif

cl::PrintOptionValues();
Expand Down
84 changes: 26 additions & 58 deletions src/debuginfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,6 @@ void jl_add_code_in_flight(StringRef name, jl_code_instance_t *codeinst, const D
}


#ifdef _OS_WINDOWS_
#if defined(_CPU_X86_64_)
void *lookupWriteAddressFor(RTDyldMemoryManager *memmgr, void *rt_addr);
#endif
#endif

#if defined(_OS_WINDOWS_)
static void create_PRUNTIME_FUNCTION(uint8_t *Code, size_t Size, StringRef fnname,
uint8_t *Section, size_t Allocated, uint8_t *UnwindData)
Expand Down Expand Up @@ -177,7 +171,14 @@ struct revcomp {
};


class JuliaJITEventListener
// Central registry for resolving function addresses to `jl_method_instance_t`s and
// originating `ObjectFile`s (for the DWARF debug info).
//
// A global singleton instance is notified by the JIT whenever a new object is emitted,
// and later queried by the various function info APIs. We also use the chance to handle
// some platform-specific unwind info registration (which is unrelated to the query
// functionality).
class JITObjectRegistry
{
std::map<size_t, ObjectInfo, revcomp> objectmap;
std::map<size_t, std::pair<size_t, jl_method_instance_t *>, revcomp> linfomap;
Expand All @@ -194,44 +195,16 @@ class JuliaJITEventListener
return linfo;
}

void NotifyObjectEmitted(const object::ObjectFile &Object,
const RuntimeDyld::LoadedObjectInfo &L,
RTDyldMemoryManager *memmgr)
void registerJITObject(const object::ObjectFile &Object,
std::function<uint64_t(const StringRef &)> getLoadAddress,
std::function<void*(void*)> lookupWriteAddress)
{
jl_ptls_t ptls = jl_current_task->ptls;
// This function modify codeinst->fptr in GC safe region.
// This should be fine since the GC won't scan this field.
int8_t gc_state = jl_gc_safe_enter(ptls);

auto SavedObject = L.getObjectForDebug(Object).takeBinary();
// If the debug object is unavailable, save (a copy of) the original object
// for our backtraces.
// This copy seems unfortunate, but there doesn't seem to be a way to take
// ownership of the original buffer.
if (!SavedObject.first) {
auto NewBuffer = MemoryBuffer::getMemBufferCopy(
Object.getData(), Object.getFileName());
auto NewObj = cantFail(object::ObjectFile::createObjectFile(NewBuffer->getMemBufferRef()));
SavedObject = std::make_pair(std::move(NewObj), std::move(NewBuffer));
}
const object::ObjectFile &debugObj = *SavedObject.first.release();
SavedObject.second.release();

object::section_iterator EndSection = debugObj.section_end();
StringMap<object::SectionRef> loadedSections;
for (const object::SectionRef &lSection: Object.sections()) {
auto sName = lSection.getName();
if (sName) {
bool inserted = loadedSections.insert(std::make_pair(*sName, lSection)).second;
assert(inserted); (void)inserted;
}
}
auto getLoadAddress = [&] (const StringRef &sName) -> uint64_t {
auto search = loadedSections.find(sName);
if (search == loadedSections.end())
return 0;
return L.getSectionLoadAddress(search->second);
};
object::section_iterator EndSection = Object.section_end();

#ifdef _CPU_ARM_
// ARM does not have/use .eh_frame
Expand Down Expand Up @@ -290,7 +263,7 @@ class JuliaJITEventListener
uint8_t *UnwindData = NULL;
#if defined(_CPU_X86_64_)
uint8_t *catchjmp = NULL;
for (const object::SymbolRef &sym_iter : debugObj.symbols()) {
for (const object::SymbolRef &sym_iter : Object.symbols()) {
StringRef sName = cantFail(sym_iter.getName());
uint8_t **pAddr = NULL;
if (sName.equals("__UnwindData")) {
Expand All @@ -313,9 +286,8 @@ class JuliaJITEventListener
SectionAddrCheck = SectionAddr;
SectionLoadCheck = SectionLoadAddr;
SectionWriteCheck = SectionLoadAddr;
if (memmgr)
SectionWriteCheck = (uintptr_t)lookupWriteAddressFor(memmgr,
(void*)SectionLoadAddr);
if (lookupWriteAddress)
SectionWriteCheck = (uintptr_t)lookupWriteAddress((void*)SectionLoadAddr);
Addr += SectionWriteCheck - SectionLoadAddr;
*pAddr = (uint8_t*)Addr;
}
Expand Down Expand Up @@ -343,7 +315,7 @@ class JuliaJITEventListener
#endif // defined(_OS_X86_64_)
#endif // defined(_OS_WINDOWS_)

auto symbols = object::computeSymbolSizes(debugObj);
auto symbols = object::computeSymbolSizes(Object);
bool first = true;
for (const auto &sym_size : symbols) {
const object::SymbolRef &sym_iter = sym_size.first;
Expand Down Expand Up @@ -380,7 +352,7 @@ class JuliaJITEventListener
if (codeinst)
linfomap[Addr] = std::make_pair(Size, codeinst->def);
if (first) {
ObjectInfo tmp = {&debugObj,
ObjectInfo tmp = {&Object,
(size_t)SectionSize,
(ptrdiff_t)(SectionAddr - SectionLoadAddr),
*Section,
Expand All @@ -394,22 +366,18 @@ class JuliaJITEventListener
jl_gc_safe_leave(ptls, gc_state);
}

// must implement if we ever start freeing code
// virtual void NotifyFreeingObject(const ObjectImage &Object) {}
// virtual void NotifyFreeingObject(const object::ObjectFile &Obj) {}

std::map<size_t, ObjectInfo, revcomp>& getObjectMap() JL_NOTSAFEPOINT
{
return objectmap;
}
};

static JuliaJITEventListener jl_jit_events;
JL_DLLEXPORT void ORCNotifyObjectEmitted(const object::ObjectFile &Object,
const RuntimeDyld::LoadedObjectInfo &L,
RTDyldMemoryManager *memmgr)
static JITObjectRegistry jl_jit_object_registry;
void jl_register_jit_object(const object::ObjectFile &Object,
std::function<uint64_t(const StringRef &)> getLoadAddress,
std::function<void *(void *)> lookupWriteAddress)
{
jl_jit_events.NotifyObjectEmitted(Object, L, memmgr);
jl_jit_object_registry.registerJITObject(Object, getLoadAddress, lookupWriteAddress);
}

// TODO: convert the safe names from aotcomile.cpp:makeSafeName back into symbols
Expand Down Expand Up @@ -1178,7 +1146,7 @@ int jl_DI_for_fptr(uint64_t fptr, uint64_t *symsize, int64_t *slide,
{
int found = 0;
uv_rwlock_wrlock(&threadsafe);
std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_events.getObjectMap();
std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_object_registry.getObjectMap();
std::map<size_t, ObjectInfo, revcomp>::iterator fit = objmap.lower_bound(fptr);

if (symsize)
Expand Down Expand Up @@ -1212,7 +1180,7 @@ extern "C" JL_DLLEXPORT int jl_getFunctionInfo_impl(jl_frame_t **frames_out, siz
int64_t slide;
uint64_t symsize;
if (jl_DI_for_fptr(pointer, &symsize, &slide, &Section, &context)) {
frames[0].linfo = jl_jit_events.lookupLinfo(pointer);
frames[0].linfo = jl_jit_object_registry.lookupLinfo(pointer);
int nf = lookup_pointer(Section, context, frames_out, pointer, slide, true, noInline);
return nf;
}
Expand All @@ -1221,7 +1189,7 @@ extern "C" JL_DLLEXPORT int jl_getFunctionInfo_impl(jl_frame_t **frames_out, siz

extern "C" jl_method_instance_t *jl_gdblookuplinfo(void *p) JL_NOTSAFEPOINT
{
return jl_jit_events.lookupLinfo((size_t)p);
return jl_jit_object_registry.lookupLinfo((size_t)p);
}

#if (defined(_OS_LINUX_) || defined(_OS_FREEBSD_) || (defined(_OS_DARWIN_) && defined(LLVM_SHLIB)))
Expand Down Expand Up @@ -1643,7 +1611,7 @@ uint64_t jl_getUnwindInfo_impl(uint64_t dwAddr)
{
// Might be called from unmanaged thread
uv_rwlock_rdlock(&threadsafe);
std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_events.getObjectMap();
std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_object_registry.getObjectMap();
std::map<size_t, ObjectInfo, revcomp>::iterator it = objmap.lower_bound(dwAddr);
uint64_t ipstart = 0; // ip of the start of the section (if found)
if (it != objmap.end() && dwAddr < it->first + it->second.SectionSize) {
Expand Down
Loading

0 comments on commit 955d427

Please sign in to comment.