Skip to content

Commit 323ac85

Browse files
committed
ARM: fold prologue/epilogue sp updates into push/pop for code size
ARM prologues usually look like: push {r7, lr} sub sp, sp, brson#4 If code size is extremely important, this can be optimised to the single instruction: push {r6, r7, lr} where we don't actually care about the contents of r6, but pushing it subtracts 4 from sp as a side effect. This should implement such a conversion, predicated on the "minsize" function attribute (-Oz) since I've yet to find any code it actually makes faster. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@194264 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 2b01682 commit 323ac85

5 files changed

Lines changed: 292 additions & 32 deletions

File tree

lib/Target/ARM/ARMBaseInstrInfo.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,6 +1857,103 @@ void llvm::emitARMRegPlusImmediate(MachineBasicBlock &MBB,
18571857
}
18581858
}
18591859

1860+
bool llvm::tryFoldSPUpdateIntoPushPop(MachineFunction &MF,
1861+
MachineInstr *MI,
1862+
unsigned NumBytes) {
1863+
// This optimisation potentially adds lots of load and store
1864+
// micro-operations, it's only really a great benefit to code-size.
1865+
if (!MF.getFunction()->hasFnAttribute(Attribute::MinSize))
1866+
return false;
1867+
1868+
// If only one register is pushed/popped, LLVM can use an LDR/STR
1869+
// instead. We can't modify those so make sure we're dealing with an
1870+
// instruction we understand.
1871+
bool IsPop = isPopOpcode(MI->getOpcode());
1872+
bool IsPush = isPushOpcode(MI->getOpcode());
1873+
if (!IsPush && !IsPop)
1874+
return false;
1875+
1876+
bool IsVFPPushPop = MI->getOpcode() == ARM::VSTMDDB_UPD ||
1877+
MI->getOpcode() == ARM::VLDMDIA_UPD;
1878+
bool IsT1PushPop = MI->getOpcode() == ARM::tPUSH ||
1879+
MI->getOpcode() == ARM::tPOP ||
1880+
MI->getOpcode() == ARM::tPOP_RET;
1881+
1882+
assert((IsT1PushPop || (MI->getOperand(0).getReg() == ARM::SP &&
1883+
MI->getOperand(1).getReg() == ARM::SP)) &&
1884+
"trying to fold sp update into non-sp-updating push/pop");
1885+
1886+
// The VFP push & pop act on D-registers, so we can only fold an adjustment
1887+
// by a multiple of 8 bytes in correctly. Similarly rN is 4-bytes. Don't try
1888+
// if this is violated.
1889+
if (NumBytes % (IsVFPPushPop ? 8 : 4) != 0)
1890+
return false;
1891+
1892+
// ARM and Thumb2 push/pop insts have explicit "sp, sp" operands (+
1893+
// pred) so the list starts at 4. Thumb1 starts after the predicate.
1894+
int RegListIdx = IsT1PushPop ? 2 : 4;
1895+
1896+
// Calculate the space we'll need in terms of registers.
1897+
unsigned FirstReg = MI->getOperand(RegListIdx).getReg();
1898+
unsigned RD0Reg, RegsNeeded;
1899+
if (IsVFPPushPop) {
1900+
RD0Reg = ARM::D0;
1901+
RegsNeeded = NumBytes / 8;
1902+
} else {
1903+
RD0Reg = ARM::R0;
1904+
RegsNeeded = NumBytes / 4;
1905+
}
1906+
1907+
// We're going to have to strip all list operands off before
1908+
// re-adding them since the order matters, so save the existing ones
1909+
// for later.
1910+
SmallVector<MachineOperand, 4> RegList;
1911+
for (int i = MI->getNumOperands() - 1; i >= RegListIdx; --i)
1912+
RegList.push_back(MI->getOperand(i));
1913+
1914+
MachineBasicBlock *MBB = MI->getParent();
1915+
const TargetRegisterInfo *TRI = MF.getRegInfo().getTargetRegisterInfo();
1916+
1917+
// Now try to find enough space in the reglist to allocate NumBytes.
1918+
for (unsigned CurReg = FirstReg - 1; CurReg >= RD0Reg && RegsNeeded;
1919+
--CurReg, --RegsNeeded) {
1920+
if (!IsPop) {
1921+
// Pushing any register is completely harmless, mark the
1922+
// register involved as undef since we don't care about it in
1923+
// the slightest.
1924+
RegList.push_back(MachineOperand::CreateReg(CurReg, false, false,
1925+
false, false, true));
1926+
continue;
1927+
}
1928+
1929+
// However, we can only pop an extra register if it's not live. Otherwise we
1930+
// might clobber a return value register. We assume that once we find a live
1931+
// return register all lower ones will be too so there's no use proceeding.
1932+
if (MBB->computeRegisterLiveness(TRI, CurReg, MI) !=
1933+
MachineBasicBlock::LQR_Dead)
1934+
return false;
1935+
1936+
// Mark the unimportant registers as <def,dead> in the POP.
1937+
RegList.push_back(MachineOperand::CreateReg(CurReg, true, false, true));
1938+
}
1939+
1940+
if (RegsNeeded > 0)
1941+
return false;
1942+
1943+
// Finally we know we can profitably perform the optimisation so go
1944+
// ahead: strip all existing registers off and add them back again
1945+
// in the right order.
1946+
for (int i = MI->getNumOperands() - 1; i >= RegListIdx; --i)
1947+
MI->RemoveOperand(i);
1948+
1949+
// Add the complete list back in.
1950+
MachineInstrBuilder MIB(MF, &*MI);
1951+
for (int i = RegList.size() - 1; i >= 0; --i)
1952+
MIB.addOperand(RegList[i]);
1953+
1954+
return true;
1955+
}
1956+
18601957
bool llvm::rewriteARMFrameIndex(MachineInstr &MI, unsigned FrameRegIdx,
18611958
unsigned FrameReg, int &Offset,
18621959
const ARMBaseInstrInfo &TII) {

lib/Target/ARM/ARMBaseInstrInfo.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,17 @@ bool isIndirectBranchOpcode(int Opc) {
362362
return Opc == ARM::BX || Opc == ARM::MOVPCRX || Opc == ARM::tBRIND;
363363
}
364364

365+
static inline bool isPopOpcode(int Opc) {
366+
return Opc == ARM::tPOP_RET || Opc == ARM::LDMIA_RET ||
367+
Opc == ARM::t2LDMIA_RET || Opc == ARM::tPOP || Opc == ARM::LDMIA_UPD ||
368+
Opc == ARM::t2LDMIA_UPD || Opc == ARM::VLDMDIA_UPD;
369+
}
370+
371+
static inline bool isPushOpcode(int Opc) {
372+
return Opc == ARM::tPUSH || Opc == ARM::t2STMDB_UPD ||
373+
Opc == ARM::STMDB_UPD || Opc == ARM::VSTMDDB_UPD;
374+
}
375+
365376
/// getInstrPredicate - If instruction is predicated, returns its predicate
366377
/// condition, otherwise returns AL. It also returns the condition code
367378
/// register by reference.
@@ -401,6 +412,13 @@ void emitThumbRegPlusImmediate(MachineBasicBlock &MBB,
401412
const ARMBaseRegisterInfo& MRI,
402413
unsigned MIFlags = 0);
403414

415+
/// Tries to add registers to the reglist of a given base-updating
416+
/// push/pop instruction to adjust the stack by an additional
417+
/// NumBytes. This can save a few bytes per function in code-size, but
418+
/// obviously generates more memory traffic. As such, it only takes
419+
/// effect in functions being optimised for size.
420+
bool tryFoldSPUpdateIntoPushPop(MachineFunction &MF, MachineInstr *MI,
421+
unsigned NumBytes);
404422

405423
/// rewriteARMFrameIndex / rewriteT2FrameIndex -
406424
/// Rewrite MI to access 'Offset' bytes from the FP. Return false if the

lib/Target/ARM/ARMFrameLowering.cpp

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,7 @@ static bool isCSRestore(MachineInstr *MI,
9393
const ARMBaseInstrInfo &TII,
9494
const uint16_t *CSRegs) {
9595
// Integer spill area is handled with "pop".
96-
if (MI->getOpcode() == ARM::LDMIA_RET ||
97-
MI->getOpcode() == ARM::t2LDMIA_RET ||
98-
MI->getOpcode() == ARM::LDMIA_UPD ||
99-
MI->getOpcode() == ARM::t2LDMIA_UPD ||
100-
MI->getOpcode() == ARM::VLDMDIA_UPD) {
96+
if (isPopOpcode(MI->getOpcode())) {
10197
// The first two operands are predicates. The last two are
10298
// imp-def and imp-use of SP. Check everything in between.
10399
for (int i = 5, e = MI->getNumOperands(); i != e; ++i)
@@ -221,42 +217,37 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF) const {
221217
}
222218

223219
// Move past area 1.
224-
if (GPRCS1Size > 0) MBBI++;
220+
MachineBasicBlock::iterator LastPush = MBB.end(), FramePtrPush;
221+
if (GPRCS1Size > 0)
222+
FramePtrPush = LastPush = MBBI++;
225223

226224
// Determine starting offsets of spill areas.
227225
bool HasFP = hasFP(MF);
228226
unsigned DPRCSOffset = NumBytes - (GPRCS1Size + GPRCS2Size + DPRCSSize);
229227
unsigned GPRCS2Offset = DPRCSOffset + DPRCSSize;
230228
unsigned GPRCS1Offset = GPRCS2Offset + GPRCS2Size;
231-
if (HasFP)
229+
int FramePtrOffsetInPush = 0;
230+
if (HasFP) {
231+
FramePtrOffsetInPush = MFI->getObjectOffset(FramePtrSpillFI) + GPRCS1Size;
232232
AFI->setFramePtrSpillOffset(MFI->getObjectOffset(FramePtrSpillFI) +
233233
NumBytes);
234+
}
234235
AFI->setGPRCalleeSavedArea1Offset(GPRCS1Offset);
235236
AFI->setGPRCalleeSavedArea2Offset(GPRCS2Offset);
236237
AFI->setDPRCalleeSavedAreaOffset(DPRCSOffset);
237238

238-
// Set FP to point to the stack slot that contains the previous FP.
239-
// For iOS, FP is R7, which has now been stored in spill area 1.
240-
// Otherwise, if this is not iOS, all the callee-saved registers go
241-
// into spill area 1, including the FP in R11. In either case, it is
242-
// now safe to emit this assignment.
243-
if (HasFP) {
244-
int FramePtrOffset = MFI->getObjectOffset(FramePtrSpillFI) + GPRCS1Size;
245-
emitRegPlusImmediate(!AFI->isThumbFunction(), MBB, MBBI, dl, TII,
246-
FramePtr, ARM::SP, FramePtrOffset,
247-
MachineInstr::FrameSetup);
248-
}
249-
250239
// Move past area 2.
251-
if (GPRCS2Size > 0) MBBI++;
240+
if (GPRCS2Size > 0) {
241+
LastPush = MBBI++;
242+
}
252243

253244
// Move past area 3.
254245
if (DPRCSSize > 0) {
255-
MBBI++;
246+
LastPush = MBBI++;
256247
// Since vpush register list cannot have gaps, there may be multiple vpush
257248
// instructions in the prologue.
258249
while (MBBI->getOpcode() == ARM::VSTMDDB_UPD)
259-
MBBI++;
250+
LastPush = MBBI++;
260251
}
261252

262253
// Move past the aligned DPRCS2 area.
@@ -272,8 +263,12 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF) const {
272263

273264
if (NumBytes) {
274265
// Adjust SP after all the callee-save spills.
275-
emitSPUpdate(isARM, MBB, MBBI, dl, TII, -NumBytes,
276-
MachineInstr::FrameSetup);
266+
if (tryFoldSPUpdateIntoPushPop(MF, LastPush, NumBytes))
267+
FramePtrOffsetInPush += NumBytes;
268+
else
269+
emitSPUpdate(isARM, MBB, MBBI, dl, TII, -NumBytes,
270+
MachineInstr::FrameSetup);
271+
277272
if (HasFP && isARM)
278273
// Restore from fp only in ARM mode: e.g. sub sp, r7, #24
279274
// Note it's not safe to do this in Thumb2 mode because it would have
@@ -286,6 +281,18 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF) const {
286281
AFI->setShouldRestoreSPFromFP(true);
287282
}
288283

284+
// Set FP to point to the stack slot that contains the previous FP.
285+
// For iOS, FP is R7, which has now been stored in spill area 1.
286+
// Otherwise, if this is not iOS, all the callee-saved registers go
287+
// into spill area 1, including the FP in R11. In either case, it
288+
// is in area one and the adjustment needs to take place just after
289+
// that push.
290+
if (HasFP)
291+
emitRegPlusImmediate(!AFI->isThumbFunction(), MBB, ++FramePtrPush, dl, TII,
292+
FramePtr, ARM::SP, FramePtrOffsetInPush,
293+
MachineInstr::FrameSetup);
294+
295+
289296
if (STI.isTargetELF() && hasFP(MF))
290297
MFI->setOffsetAdjustment(MFI->getOffsetAdjustment() -
291298
AFI->getFramePtrSpillOffset());
@@ -380,12 +387,17 @@ void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
380387
if (NumBytes != 0)
381388
emitSPUpdate(isARM, MBB, MBBI, dl, TII, NumBytes);
382389
} else {
390+
MachineBasicBlock::iterator FirstPop = MBBI;
391+
383392
// Unwind MBBI to point to first LDR / VLDRD.
384393
const uint16_t *CSRegs = RegInfo->getCalleeSavedRegs(&MF);
385394
if (MBBI != MBB.begin()) {
386-
do
395+
do {
396+
if (isPopOpcode(MBBI->getOpcode()))
397+
FirstPop = MBBI;
398+
387399
--MBBI;
388-
while (MBBI != MBB.begin() && isCSRestore(MBBI, TII, CSRegs));
400+
} while (MBBI != MBB.begin() && isCSRestore(MBBI, TII, CSRegs));
389401
if (!isCSRestore(MBBI, TII, CSRegs))
390402
++MBBI;
391403
}
@@ -429,8 +441,8 @@ void ARMFrameLowering::emitEpilogue(MachineFunction &MF,
429441
ARM::SP)
430442
.addReg(FramePtr));
431443
}
432-
} else if (NumBytes)
433-
emitSPUpdate(isARM, MBB, MBBI, dl, TII, NumBytes);
444+
} else if (NumBytes && !tryFoldSPUpdateIntoPushPop(MF, FirstPop, NumBytes))
445+
emitSPUpdate(isARM, MBB, MBBI, dl, TII, NumBytes);
434446

435447
// Increment past our save areas.
436448
if (AFI->getDPRCalleeSavedAreaSize()) {

lib/Target/ARM/Thumb1FrameLowering.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,17 @@ void Thumb1FrameLowering::emitPrologue(MachineFunction &MF) const {
164164
AFI->setDPRCalleeSavedAreaOffset(DPRCSOffset);
165165
NumBytes = DPRCSOffset;
166166

167+
int FramePtrOffsetInBlock = 0;
168+
if (tryFoldSPUpdateIntoPushPop(MF, prior(MBBI), NumBytes)) {
169+
FramePtrOffsetInBlock = NumBytes;
170+
NumBytes = 0;
171+
}
172+
167173
// Adjust FP so it point to the stack slot that contains the previous FP.
168174
if (HasFP) {
169-
int FramePtrOffset = MFI->getObjectOffset(FramePtrSpillFI) + GPRCS1Size;
175+
FramePtrOffsetInBlock += MFI->getObjectOffset(FramePtrSpillFI) + GPRCS1Size;
170176
AddDefaultPred(BuildMI(MBB, MBBI, dl, TII.get(ARM::tADDrSPi), FramePtr)
171-
.addReg(ARM::SP).addImm(FramePtrOffset / 4)
177+
.addReg(ARM::SP).addImm(FramePtrOffsetInBlock / 4)
172178
.setMIFlags(MachineInstr::FrameSetup));
173179
if (NumBytes > 508)
174180
// If offset is > 508 then sp cannot be adjusted in a single instruction,
@@ -292,8 +298,9 @@ void Thumb1FrameLowering::emitEpilogue(MachineFunction &MF,
292298
&MBB.front() != MBBI &&
293299
prior(MBBI)->getOpcode() == ARM::tPOP) {
294300
MachineBasicBlock::iterator PMBBI = prior(MBBI);
295-
emitSPUpdate(MBB, PMBBI, TII, dl, *RegInfo, NumBytes);
296-
} else
301+
if (!tryFoldSPUpdateIntoPushPop(MF, PMBBI, NumBytes))
302+
emitSPUpdate(MBB, PMBBI, TII, dl, *RegInfo, NumBytes);
303+
} else if (!tryFoldSPUpdateIntoPushPop(MF, MBBI, NumBytes))
297304
emitSPUpdate(MBB, MBBI, TII, dl, *RegInfo, NumBytes);
298305
}
299306
}

0 commit comments

Comments
 (0)