This recipe talks about frame lowering for target architecture. Frame lowering involves emitting the prologue and epilogue of the function call.
The following functions are defined in the TOYFrameLowering.cpp
file in the lib/Target/TOY
folder:
emitPrologue
function can be defined as follows:void TOYFrameLowering::emitPrologue(MachineFunction &MF) const { const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); MachineBasicBlock &MBB = MF.front(); MachineBasicBlock::iterator MBBI = MBB.begin(); DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); uint64_t StackSize = computeStackSize(MF); if (!StackSize) { return; } unsigned StackReg = TOY::SP; unsigned OffsetReg = materializeOffset(MF, MBB, MBBI, (unsigned)StackSize); if (OffsetReg) { BuildMI(MBB, MBBI, dl, TII.get(TOY::SUBrr), StackReg) .addReg(StackReg) .addReg(OffsetReg) .setMIFlag(MachineInstr::FrameSetup); } else { BuildMI(MBB, MBBI, dl, TII.get(TOY::SUBri), StackReg) .addReg(StackReg) .addImm(StackSize) .setMIFlag(MachineInstr::FrameSetup); } }
emitEpilogue
function can be defined like this:void TOYFrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); DebugLoc dl = MBBI->getDebugLoc(); uint64_t StackSize = computeStackSize(MF); if (!StackSize) { return; } unsigned StackReg = TOY::SP; unsigned OffsetReg = materializeOffset(MF, MBB, MBBI, (unsigned)StackSize); if (OffsetReg) { BuildMI(MBB, MBBI, dl, TII.get(TOY::ADDrr), StackReg) .addReg(StackReg) .addReg(OffsetReg) .setMIFlag(MachineInstr::FrameSetup); } else { BuildMI(MBB, MBBI, dl, TII.get(TOY::ADDri), StackReg) .addReg(StackReg) .addImm(StackSize) .setMIFlag(MachineInstr::FrameSetup); } }
ADD
stack operation:static unsigned materializeOffset(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, unsigned Offset) { const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); const uint64_t MaxSubImm = 0xfff; if (Offset <= MaxSubImm) { return 0; } else { unsigned OffsetReg = TOY::R2; unsigned OffsetLo = (unsigned)(Offset & 0xffff); unsigned OffsetHi = (unsigned)((Offset & 0xffff0000) >> 16); BuildMI(MBB, MBBI, dl, TII.get(TOY::MOVLOi16), OffsetReg) .addImm(OffsetLo) .setMIFlag(MachineInstr::FrameSetup); if (OffsetHi) { BuildMI(MBB, MBBI, dl, TII.get(TOY::MOVHIi16), OffsetReg) .addReg(OffsetReg) .addImm(OffsetHi) .setMIFlag(MachineInstr::FrameSetup); } return OffsetReg; } }
uint64_t TOYFrameLowering::computeStackSize(MachineFunction &MF) const { MachineFrameInfo *MFI = MF.getFrameInfo(); uint64_t StackSize = MFI->getStackSize(); unsigned StackAlign = getStackAlignment(); if (StackAlign > 0) { StackSize = RoundUpToAlignment(StackSize, StackAlign); } return StackSize; }
The emitPrologue
function first computes the stack size to determine whether the prologue is required at all. Then it adjusts the stack pointer by calculating the offset. For the epilogue, it first checks whether the epilogue is required or not. Then it restores the stack pointer to what it was at the beginning of the function.
For example, consider this input IR:
%p = alloca i32, align 4 store i32 2, i32* %p %b = load i32* %p, align 4 %c = add nsw i32 %a, %b
The TOY assembly generated will look like this:
sub sp, sp, #4 ; prologue movw r1, #2 str r1, [sp] add r0, r0, #2 add sp, sp, #4 ; epilogue
3.135.204.0