Skip to content

Commit e4c1dfe

Browse files
committed
[X86] Create extra prolog/epilog for stack realignment
The base pointer register is reserved by compiler when there is dynamic size alloca and stack realign in a function. However the base pointer register is not defined in X86 ABI, so user can use this register in inline assembly. The inline assembly would clobber base pointer register without being awared by user. This patch is to create extra prolog to save the stack pointer to a scratch register and use this register to reference argument from stack. For some calling convention (e.g. regcall), there may be few scratch register. Below is the example code for such case. ``` extern int bar(void *p); long long foo(size_t size, char c, int id) { __attribute__((__aligned__(64))) int a; char *p = (char *)alloca(size); asm volatile ("nop"::"S"(405):); asm volatile ("movl %0, %1"::"r"(id), "m"(a):); p[2] = 8; memset(p, c, size); return bar(p); } ``` And below prolog/epilog will be emit for this case. ``` leal 4(%esp), %ebx .cfi_def_cfa %ebx, 0 andl $-128, %esp pushl -4(%ebx) ... leal 4(%ebx), %esp .cfi_def_cfa %esp, 4 ``` Differential Revision: https://reviews.llvm.org/D145650
1 parent 6a0f2e5 commit e4c1dfe

15 files changed

+529
-82
lines changed

llvm/lib/Target/X86/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ endif()
2626
add_public_tablegen_target(X86CommonTableGen)
2727

2828
set(sources
29+
X86ArgumentStackSlotRebase.cpp
2930
X86AsmPrinter.cpp
3031
X86AvoidTrailingCall.cpp
3132
X86CallFrameOptimization.cpp

llvm/lib/Target/X86/X86.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,13 @@ FunctionPass *createX86LoadValueInjectionLoadHardeningPass();
166166
FunctionPass *createX86LoadValueInjectionRetHardeningPass();
167167
FunctionPass *createX86SpeculativeLoadHardeningPass();
168168
FunctionPass *createX86SpeculativeExecutionSideEffectSuppression();
169+
FunctionPass *createX86ArgumentStackSlotPass();
169170

170171
void initializeEvexToVexInstPassPass(PassRegistry &);
171172
void initializeFPSPass(PassRegistry &);
172173
void initializeFixupBWInstPassPass(PassRegistry &);
173174
void initializeFixupLEAPassPass(PassRegistry &);
175+
void initializeX86ArgumentStackSlotPassPass(PassRegistry &);
174176
void initializeX86FixupInstTuningPassPass(PassRegistry &);
175177
void initializeWinEHStatePassPass(PassRegistry &);
176178
void initializeX86AvoidSFBPassPass(PassRegistry &);
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
//===---- X86ArgumentStackSlotRebase.cpp - rebase argument stack slot -----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This pass replace the frame register with a GPR virtual register and set
10+
// the stack offset for each instruction which reference argument from stack.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "X86.h"
15+
#include "X86InstrBuilder.h"
16+
#include "X86MachineFunctionInfo.h"
17+
#include "X86RegisterInfo.h"
18+
#include "X86Subtarget.h"
19+
#include "llvm/CodeGen/MachineBasicBlock.h"
20+
#include "llvm/CodeGen/MachineFrameInfo.h"
21+
#include "llvm/CodeGen/MachineFunction.h"
22+
#include "llvm/CodeGen/MachineFunctionPass.h"
23+
#include "llvm/CodeGen/MachineInstr.h"
24+
#include "llvm/CodeGen/MachineOperand.h"
25+
#include "llvm/CodeGen/MachineRegisterInfo.h"
26+
#include "llvm/CodeGen/TargetOpcodes.h"
27+
#include "llvm/CodeGen/TargetRegisterInfo.h"
28+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
29+
#include "llvm/IR/Attributes.h"
30+
#include "llvm/IR/Function.h"
31+
#include "llvm/InitializePasses.h"
32+
#include "llvm/Pass.h"
33+
34+
using namespace llvm;
35+
36+
#define DEBUG_TYPE "x86argumentstackrebase"
37+
38+
namespace {
39+
40+
class X86ArgumentStackSlotPass : public MachineFunctionPass {
41+
42+
public:
43+
static char ID; // Pass identification, replacement for typeid
44+
45+
explicit X86ArgumentStackSlotPass() : MachineFunctionPass(ID) {
46+
initializeX86ArgumentStackSlotPassPass(*PassRegistry::getPassRegistry());
47+
}
48+
49+
bool runOnMachineFunction(MachineFunction &MF) override;
50+
51+
void getAnalysisUsage(AnalysisUsage &AU) const override {
52+
AU.setPreservesCFG();
53+
MachineFunctionPass::getAnalysisUsage(AU);
54+
}
55+
};
56+
57+
} // end anonymous namespace
58+
59+
char X86ArgumentStackSlotPass::ID = 0;
60+
61+
INITIALIZE_PASS(X86ArgumentStackSlotPass, DEBUG_TYPE, "Argument Stack Rebase",
62+
false, false)
63+
64+
FunctionPass *llvm::createX86ArgumentStackSlotPass() {
65+
return new X86ArgumentStackSlotPass();
66+
}
67+
68+
static Register getArgBaseReg(MachineFunction &MF) {
69+
MachineRegisterInfo &MRI = MF.getRegInfo();
70+
const X86Subtarget &STI = MF.getSubtarget<X86Subtarget>();
71+
const Function &F = MF.getFunction();
72+
CallingConv::ID CC = F.getCallingConv();
73+
Register NoReg;
74+
const TargetRegisterClass *RC = nullptr;
75+
switch (CC) {
76+
// We need a virtual register in case there is inline assembly
77+
// clobber argument base register.
78+
case CallingConv::C:
79+
RC = STI.is64Bit() ? &X86::GR64_ArgRefRegClass : &X86::GR32_ArgRefRegClass;
80+
break;
81+
case CallingConv::X86_RegCall:
82+
// FIXME: For regcall there is no scratch register on 32-bit target.
83+
// We may use a callee saved register as argument base register and
84+
// save it before being changed as base pointer. We need DW_CFA to
85+
// indicate where the callee saved register is saved, so that it can
86+
// be correctly unwind.
87+
// push ebx
88+
// mov ebx, esp
89+
// and esp, -128
90+
// ...
91+
// pop ebx
92+
// ret
93+
RC = STI.is64Bit() ? &X86::GR64_ArgRefRegClass : nullptr;
94+
break;
95+
// TODO: Refine register class for each calling convention.
96+
default:
97+
break;
98+
}
99+
if (RC)
100+
return MRI.createVirtualRegister(RC);
101+
else
102+
return NoReg;
103+
}
104+
105+
bool X86ArgumentStackSlotPass::runOnMachineFunction(MachineFunction &MF) {
106+
const Function &F = MF.getFunction();
107+
MachineFrameInfo &MFI = MF.getFrameInfo();
108+
const X86Subtarget &STI = MF.getSubtarget<X86Subtarget>();
109+
const X86RegisterInfo *TRI = STI.getRegisterInfo();
110+
const X86InstrInfo *TII = STI.getInstrInfo();
111+
X86MachineFunctionInfo *X86FI = MF.getInfo<X86MachineFunctionInfo>();
112+
bool Changed = false;
113+
114+
if (F.hasFnAttribute(Attribute::Naked))
115+
return false;
116+
// Only support Linux
117+
if (!STI.isTargetLinux())
118+
return false;
119+
if (!TRI->hasBasePointer(MF))
120+
return false;
121+
122+
Register BasePtr = TRI->getBaseRegister();
123+
auto IsBaseRegisterClobbered = [&]() {
124+
for (MachineBasicBlock &MBB : MF) {
125+
for (MachineInstr &MI : MBB) {
126+
if (!MI.isInlineAsm())
127+
continue;
128+
for (MachineOperand &MO : MI.operands()) {
129+
if (!MO.isReg())
130+
continue;
131+
Register Reg = MO.getReg();
132+
if (!Register::isPhysicalRegister(Reg))
133+
continue;
134+
if (TRI->isSuperOrSubRegisterEq(BasePtr, Reg))
135+
return true;
136+
}
137+
}
138+
}
139+
return false;
140+
};
141+
if (!IsBaseRegisterClobbered())
142+
return false;
143+
144+
Register ArgBaseReg = getArgBaseReg(MF);
145+
if (!ArgBaseReg.isValid())
146+
return false;
147+
// leal 4(%esp), %reg
148+
// FIXME: will the instruction be duplicated or eliminated? Should
149+
// define a pseudo instruction for it?
150+
MachineBasicBlock &MBB = MF.front();
151+
MachineBasicBlock::iterator MBBI = MBB.begin();
152+
DebugLoc DL;
153+
// Emit instruction to copy get stack pointer to a virtual register
154+
// and save the instruction to x86 machine functon info. We can get
155+
// physical register of ArgBaseReg after register allocation. The
156+
// stack slot is used to save/restore argument base pointer. We can
157+
// get the index from the instruction.
158+
unsigned SlotSize = TRI->getSlotSize();
159+
int FI = MFI.CreateSpillStackObject(SlotSize, Align(SlotSize));
160+
MachineInstr *LEA =
161+
BuildMI(MBB, MBBI, DL,
162+
TII->get(STI.is64Bit() ? X86::LEA64r : X86::LEA32r), ArgBaseReg)
163+
.addFrameIndex(FI)
164+
.addImm(1)
165+
.addUse(X86::NoRegister)
166+
.addImm(SlotSize)
167+
.addUse(X86::NoRegister)
168+
.setMIFlag(MachineInstr::FrameSetup);
169+
X86FI->setStackPtrSaveMI(LEA);
170+
171+
for (MachineBasicBlock &MBB : MF) {
172+
for (MachineInstr &MI : MBB) {
173+
int I = 0;
174+
for (MachineOperand &MO : MI.operands()) {
175+
if (MO.isFI()) {
176+
int Idx = MO.getIndex();
177+
if (!MFI.isFixedObjectIndex(Idx))
178+
continue;
179+
int64_t Offset = MFI.getObjectOffset(Idx);
180+
if (Offset < 0)
181+
continue;
182+
// TODO replace register for debug instruction
183+
if (MI.isDebugInstr())
184+
continue;
185+
// Replace frame register with argument base pointer and its offset.
186+
TRI->eliminateFrameIndex(MI.getIterator(), I, ArgBaseReg, Offset);
187+
Changed = true;
188+
}
189+
++I;
190+
}
191+
}
192+
}
193+
194+
return Changed;
195+
}

0 commit comments

Comments
 (0)