Inserting the prologue-epilogue code

Inserting the prologue-epilogue code involves stack unwinding, finalizing the function layout, saving callee-saved registers and emitting the prologue and epilogue code. It also replaces abstract frame indexes with appropriate references. This pass runs after the register allocation phase.

How to do it…

The skeleton and the important functions defined in the PrologueEpilogueInserter class are as follows:

  • The prologue epilogue inserter pass runs on a machine function, hence it inherits the MachineFunctionPass class. Its constructor initializes the pass:
    class PEI : public MachineFunctionPass {
      public:
        static char ID;
        PEI() : MachineFunctionPass(ID) {
          initializePEIPass(*PassRegistry::getPassRegistry());
        }
  • There are various helper functions defined in this class that help insert the prologue and epilogue code:
        void calculateSets(MachineFunction &Fn);
        void calculateCallsInformation(MachineFunction &Fn);
        void calculateCalleeSavedRegisters(MachineFunction &Fn);
        void insertCSRSpillsAndRestores(MachineFunction &Fn);
        void calculateFrameObjectOffsets(MachineFunction &Fn);
        void replaceFrameIndices(MachineFunction &Fn);
        void replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &Fn,
                                 int &SPAdj);
        void scavengeFrameVirtualRegs(MachineFunction &Fn);
  • The main function, insertPrologEpilogCode(), does the task of inserting the prologue and epilogue code:
        void insertPrologEpilogCode(MachineFunction &Fn);
  • The first function to execute in this pass is the runOnFunction() function. The comments in the code show the various operations carried out, such as calculating the call frame size, adjusting the stack variables, inserting the spill code for the callee-saved register for modified registers, calculating the actual frame offset, inserting the prologue and epilogue code for the function, replacing the abstract frame index with the actual offsets, and so on:
    bool PEI::runOnMachineFunction(MachineFunction &Fn) {
      const Function* F = Fn.getFunction();
      const TargetRegisterInfo *TRI = Fn.getSubtarget().getRegisterInfo();
      const TargetFrameLowering *TFI = Fn.getSubtarget().getFrameLowering();
    
      assert(!Fn.getRegInfo().getNumVirtRegs() && "Regalloc must assign all vregs");
    
      RS = TRI->requiresRegisterScavenging(Fn) ? new RegScavenger() : nullptr;
      FrameIndexVirtualScavenging = TRI->requiresFrameIndexScavenging(Fn);
    
      // Calculate the MaxCallFrameSize and AdjustsStack variables for the
      // function's frame information. Also eliminates call frame pseudo
      // instructions.
      calculateCallsInformation(Fn);
    
      // Allow the target machine to make some adjustments to the function
      // e.g. UsedPhysRegs before calculateCalleeSavedRegisters.
      TFI->processFunctionBeforeCalleeSavedScan(Fn, RS);
    
      // Scan the function for modified callee saved registers and insert spill code
      // for any callee saved registers that are modified.
      calculateCalleeSavedRegisters(Fn);
    
      // Determine placement of CSR spill/restore code:
      // place all spills in the entry block, all restores in return blocks.
      calculateSets(Fn);
    
      // Add the code to save and restore the callee saved registers
      if (!F->hasFnAttribute(Attribute::Naked))
        insertCSRSpillsAndRestores(Fn);
    
      // Allow the target machine to make final modifications to the function
      // before the frame layout is finalized.
      TFI->processFunctionBeforeFrameFinalized(Fn, RS);
    
      // Calculate actual frame offsets for all abstract stack objects...
      calculateFrameObjectOffsets(Fn);
    
      // Add prolog and epilog code to the function.  This function is required
      // to align the stack frame as necessary for any stack variables or
      // called functions.  Because of this, calculateCalleeSavedRegisters()
      // must be called before this function in order to set the AdjustsStack
      // and MaxCallFrameSize variables.
      if (!F->hasFnAttribute(Attribute::Naked))
        insertPrologEpilogCode(Fn);
    
      // Replace all MO_FrameIndex operands with physical register references
      // and actual offsets.
      replaceFrameIndices(Fn);
    
      // If register scavenging is needed, as we've enabled doing it as a
      // post-pass, scavenge the virtual registers that frame index elimination
      // inserted.
      if (TRI->requiresRegisterScavenging(Fn) && FrameIndexVirtualScavenging)
        scavengeFrameVirtualRegs(Fn);
    
      // Clear any vregs created by virtual scavenging.
      Fn.getRegInfo().clearVirtRegs();
    
      // Warn on stack size when we exceeds the given limit.
      MachineFrameInfo *MFI = Fn.getFrameInfo();
      uint64_t StackSize = MFI->getStackSize();
      if (WarnStackSize.getNumOccurrences() > 0 && WarnStackSize < StackSize) {
        DiagnosticInfoStackSize DiagStackSize(*F, StackSize);
        F->getContext().diagnose(DiagStackSize);
      }
      delete RS;
      ReturnBlocks.clear();
      return true;
    }
  • The main function that inserts prologue-epilogue code is the insertPrologEpilogCode() function. This function first takes the TargetFrameLowering object and then emits a prologue code for that function corresponding to that target. After that, for each basic block in that function, it checks whether there is a return statement. If there is a return statement, then it emits an epilogue code for that function:
    void PEI::insertPrologEpilogCode(MachineFunction &Fn) {
      const TargetFrameLowering &TFI = *Fn.getSubtarget().getFrameLowering();
    
      // Add prologue to the function.
      TFI.emitPrologue(Fn);
    
      // Add epilogue to restore the callee-save registers in each exiting block
      for (MachineFunction::iterator I = Fn.begin(), E = Fn.end(); I != E; ++I) {
        // If last instruction is a return instruction, add an epilogue
        if (!I->empty() && I->back().isReturn())
          TFI.emitEpilogue(Fn, *I);
      }
    
      // Emit additional code that is required to support segmented stacks, if
      // we've been asked for it.  This, when linked with a runtime with support
      // for segmented stacks (libgcc is one), will result in allocating stack
      // space in small chunks instead of one large contiguous block.
      if (Fn.shouldSplitStack())
        TFI.adjustForSegmentedStacks(Fn);
    
      // Emit additional code that is required to explicitly handle the stack in
      // HiPE native code (if needed) when loaded in the Erlang/OTP runtime. The
      // approach is rather similar to that of Segmented Stacks, but it uses a
      // different conditional check and another BIF for allocating more stack
      // space.
      if (Fn.getFunction()->getCallingConv() == CallingConv::HiPE)
        TFI.adjustForHiPEPrologue(Fn);
    }

How it works…

The preceding code invokes the emitEpilogue() and the emitPrologue() functions in the TargetFrameLowering class, which will be discussed in the target-specific frame lowering recipes in later chapters.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.4.191