JIT machine code deletion

Hi All,

I just implemented an often-requested feature: the ability to delete machine code out of the JIT's code buffer (the ExecutionEngine::freeMachineCodeForFunction(F) method).

The implementation uses a very general free-list mechanism for tracking free ranges in the buffer, and it works perfectly in my simple test cases designed to cover all of the code paths. However, I don't have any good "real world" way to test this thoroughly.

If you have an application for this API, please try it out. Remember that, once deleted, the memory for a function can be reused for other functions. This means you should only call this method if you *know* that execution could never reach this function again. As a sanity check, in debug builds, I memset the freed buffer with garbage to try to make it obvious if the buffer is called into.

If you don't *know* that all (e.g.) function pointers to this code are dead (which means that execution could come back to the function), you should use the ExecutionEngine::recompileAndRelinkFunction(F) method.

I'm happy to answer any questions about this functionality, as usual.

-Chris

Hi Chris,

If you don't *know* that all (e.g.) function pointers to this code are
dead (which means that execution could come back to the function), you
should use the ExecutionEngine::recompileAndRelinkFunction(F) method.

recompileAndRelinkFunction() overwrites the old machine code with a
branch to the new. Is it always guaranteed that there's space to write
the new branch instructions? A quick look suggests the x86 takes five
bytes, PowerPC 16, and the Alpha's implementation is an assert(0) but if
it did call its EmitBranchToAt() then that would be 76. Could the
original machine code be smaller?

Cheers,

Ralph.

If you don't *know* that all (e.g.) function pointers to this code are
dead (which means that execution could come back to the function), you
should use the ExecutionEngine::recompileAndRelinkFunction(F) method.

recompileAndRelinkFunction() overwrites the old machine code with a
branch to the new. Is it always guaranteed that there's space to write
the new branch instructions?

Yes.

A quick look suggests the x86 takes five
bytes, PowerPC 16,

PPC should be 4 bytes in the normal case. It is up to the backend to implement this API correctly... if it doesn't, that's a bug: please report it!

-Chris

Hi Chris,

> recompileAndRelinkFunction() overwrites the old machine code with a
> branch to the new. Is it always guaranteed that there's space to
> write the new branch instructions?

Yes.

OK.

> A quick look suggests the x86 takes five bytes, PowerPC 16,

PPC should be 4 bytes in the normal case. It is up to the backend to
implement this API correctly... if it doesn't, that's a bug: please
report it!

No, EmitBranchToAt() always emits 16 bytes.

    AtI[0] = BUILD_LIS(12, Addr >> 16); // lis r12, hi16(address)
    AtI[1] = BUILD_ORI(12, 12, Addr); // ori r12, r12, low16(address)
    AtI[2] = BUILD_MTCTR(12); // mtctr r12
    AtI[3] = BUILD_BCTR(isCall); // bctr/bctrl

but I understand now how startFunctionStub() is given StubSize and on
PowerPC it's 16 so there's no problem.

Cheers,

Ralph.

Ah, that's silly. If the source and target are within range (as they should almost always be), a simple unconditional branch should be used. Patches welcome :slight_smile:

-Chris