dibyendumajumdar / nanojit

NanoJIT is a small, cross-platform C++ library that emits machine code.
Mozilla Public License 2.0
151 stars 16 forks source link

Investigate whether the guard instruction can be removed #13

Open dibyendumajumdar opened 6 years ago

dibyendumajumdar commented 6 years ago

Right now we are telling NanoJIT to save the callee save registers. However, it is not clear whether this is actually needed or not, and whether these registers are correctly restored at function exit.

Adding these does seem to cause a lot of register spilling and restoring at branches.

This issue is related to:

https://github.com/adobe/avmplus/issues/4

Content reproduced: I have noticed that in lirasm immediately after the LIR_start instruction, there are additional instructions to save registers.

    mLir->ins0(LIR_start);
    for (int i = 0; i < nanojit::NumSavedRegs; ++i)
        mLir->insParam(i, 1);

Similarly in LirHelper - this is done done in emitStart():

    // create params for saved regs -- processor specific
    for (int i=0; i < NumSavedRegs; i++) {
        LIns *p = lirout->insParam(i, 1); (void) p;
    }

In lirasm when a fragment is ended a guard is inserted:

    mFragment->lastIns =
        mLir->insGuard(LIR_x, NULL, createGuardRecord(createSideExit()))

I noticed that the registers saved initially are popped in the guard section. Looking at the generated code it seems to me though that when the code fragment has 'ret' instructions then the guard will never be executed - so in that case the saved registers will not be restored?

Is it mandatory to include the guard?

Here is a reply on this issue from Edwin Smith:

Yes, NumSavedRegs refers to the callee-saved registers. They're treated as extra parameters and return values, causing them to be "alive" for the whole fragment, since their last use will be the exit (guard) or return instruction. Any callee-saved registers that aren't used by the register allocator should just be preserved without any spills.

I don't think the guard is required, as long as all paths through the code reach a return or non-truning call (i.e. throw), things should be fine. But my memory is foggy in that area.