Question about memory allocation in JIT

Hello! Working with LLVM JIT-compiler I found a small bug and I'd like to correct it. Namely, on some tests LLVM fails with message "JIT: Ran out of space for generated machine code!"

This error emerges because the test creates big static array. Global variables are placed into memory block for function, that is first seen using given variable. Besides, during memory allocation for function its size is not considered in any way. Although there exist a code block (in JITEmitter::startFunction), which is able to calculate function size, allocator (in DefaultJITMemoryManager::startFunctionBody) does not consider predicted function size. So lli fails, when a test uses big static array.

How would you advise to correct this bug? It is possible to allocate global variables in separate memory block(s) (in my opinion, it is not so good to place variables into executable memory area), or it is possible to improve allocator so that it considers predicted function size. However, if using size prediction, JIT calculation of exact function size may occupy time. Probably it would be better to estimate function size instead of exact calculation.

Hello! Working with LLVM JIT-compiler I found a small bug and I'd like to correct it. Namely, on some tests LLVM fails with message "JIT: Ran out of space for generated machine code!"

I'm working on a patch to fix this, although I've heard DOE may change
the MachineCodeEmitter interface to make this less necessary.

http://codereview.appspot.com/71042

This error emerges because the test creates big static array. Global variables are placed into memory block for function, that is first seen using given variable. Besides, during memory allocation for function its size is not considered in any way. Although there exist a code block (in JITEmitter::startFunction), which is able to calculate function size, allocator (in DefaultJITMemoryManager::startFunctionBody) does not consider predicted function size. So lli fails, when a test uses big static array.

According to the documentation I have read, in general it is not
possible to predict code size, but from that block of code it seems
like it works for someone using a custom memory manager that does pay
attention to size on some platform. I'm also working on a patch to
separate globals from code, but apparently Apple wants to support that
behavior, even though it breaks freeMachineCodeForFunction.

http://llvm.org/bugs/show_bug.cgi?id=4483

How would you advise to correct this bug? It is possible to allocate global variables in separate memory block(s) (in my opinion, it is not so good to place variables into executable memory area), or it is possible to improve allocator so that it considers predicted function size. However, if using size prediction, JIT calculation of exact function size may occupy time. Probably it would be better to estimate function size instead of exact calculation.

Unless there is some DOE change that I'm not aware of that totally
changes the situation, I think the right thing to do is to just fully
implement the API. I think that emitting the binary from the
MachineFunction isn't much of a bottleneck, but I don't have hard data
that says so. All of the target-specific MachineCodeEmitters are
coded to retry on failure. The problem is that right now the memory
manager doesn't know how to request more space from the OS.

Reid

Where "DOE" is "Direct Object Code Emission", described at
http://wiki.llvm.org/Direct_Object_Code_Emission

+llvmdev

> Hello! Working with LLVM JIT-compiler I found a small bug and I'd like to correct it. Namely, on some tests LLVM fails with message "JIT: Ran out of space for generated machine code!"

I'm working on a patch to fix this, although I've heard DOE may change
the MachineCodeEmitter interface to make this less necessary.

http://codereview.appspot.com/71042

Hello! Thanks a lot for the patch! I had applied it and my test stopped to break lli, but it worked incorrectly. So I fixed the patch (see below) and now it seems that my test doesn't break lli and works correctly.

If allocated block is too small for function body and globals, then it is necessary to remove globals allocated in that block from mappings. So we should backup Relocations before changes in "for (unsigned i = 0, e = Relocations.size(); i != e; ++i) {" loop, and, in case of buffer overflow, we should remove globals from mappings. Also, we should clear ConstPoolAddresses. There are only two changes in function JITEmitter::finishFunction:

--- lib/ExecutionEngine/JIT/JITEmitter.cpp (original patch)
+++ lib/ExecutionEngine/JIT/JITEmitter.cpp (my changes)
@@ -946,6 +947,7 @@
// FnEnd is the end of the function's machine code.
uint8_t *FnEnd = CurBufferPtr;

+ std::vector<MachineRelocation> BackupRelocations = Relocations;
if (!Relocations.empty()) {
CurFn = F.getFunction();
NumRelos += Relocations.size();
@@ -1028,8 +1030,18 @@
MemMgr->endFunctionBody(F.getFunction(), BufferBegin, CurBufferPtr);

if (CurBufferPtr == BufferEnd) {
- retryWithMoreMemory(F);
- return true;
+ for (unsigned i = 0, e = BackupRelocations.size(); i != e; ++i) {
+ MachineRelocation &MR = BackupRelocations[i];
+ if (MR.isGlobalValue()) {
+ void* ResultPtr = TheJIT->getPointerToGlobalIfAvailable(MR.getGlobalValue());
+ if (BufferBegin <= ResultPtr && ResultPtr < BufferEnd) {
+ TheJIT->updateGlobalMapping(MR.getGlobalValue(), 0);
+ }
+ }
+ }
+ retryWithMoreMemory(F);
+ ConstPoolAddresses.clear();
+ return true;
} else {
// Now that we've succeeded in emitting the function, reset the
// SizeEstimate

This is a known problem, and the solution I chose was to not emit
global data and code into the same buffer, since it breaks
freeMachineCodeForFunction in general, which is what
retryWithMoreMemory depends on. When that patch, which I sent to
llvm-commits last night, goes in I'll fix up this patch so that it
allocates more space for code and data.

It might still be worth applying this patch because some clients still
rely on the old behavior where code and data go together, and if they
run out of memory, they're screwed.

Reid