llvm.org GIT mirror llvm / 484fd96
Add the ShadowCallStack pass Summary: The ShadowCallStack pass instruments functions marked with the shadowcallstack attribute. The instrumented prolog saves the return address to [gs:offset] where offset is stored and updated in [gs:0]. The instrumented epilog loads/updates the return address from [gs:0] and checks that it matches the return address on the stack before returning. Reviewers: pcc, vitalybuka Reviewed By: pcc Subscribers: cryptoad, eugenis, craig.topper, mgorny, llvm-commits, kcc Differential Revision: https://reviews.llvm.org/D44802 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@329139 91177308-0d34-0410-b5e6-96231b3b80d8 Vlad Tsyrklevich 2 years ago
7 changed file(s) with 540 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
2020 add_public_tablegen_target(X86CommonTableGen)
2121
2222 set(sources
23 ShadowCallStack.cpp
2324 X86AsmPrinter.cpp
2425 X86CallFrameOptimization.cpp
2526 X86CallingConv.cpp
0 //===------- ShadowCallStack.cpp - Shadow Call Stack pass -----------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // The ShadowCallStack pass instruments function prologs/epilogs to check that
10 // the return address has not been corrupted during the execution of the
11 // function. The return address is stored in a 'shadow call stack' addressed
12 // using the %gs segment register.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #include "X86.h"
17 #include "X86InstrBuilder.h"
18 #include "X86InstrInfo.h"
19 #include "X86Subtarget.h"
20
21 #include "llvm/CodeGen/MachineFunction.h"
22 #include "llvm/CodeGen/MachineFunctionPass.h"
23 #include "llvm/CodeGen/MachineInstrBuilder.h"
24 #include "llvm/CodeGen/MachineModuleInfo.h"
25 #include "llvm/CodeGen/MachineRegisterInfo.h"
26 #include "llvm/CodeGen/Passes.h"
27 #include "llvm/Pass.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include "llvm/Target/TargetInstrInfo.h"
30
31 using namespace llvm;
32
33 namespace llvm {
34 void initializeShadowCallStackPass(PassRegistry &);
35 }
36
37 namespace {
38
39 class ShadowCallStack : public MachineFunctionPass {
40 public:
41 static char ID;
42
43 ShadowCallStack() : MachineFunctionPass(ID) {
44 initializeShadowCallStackPass(*PassRegistry::getPassRegistry());
45 }
46
47 void getAnalysisUsage(AnalysisUsage &AU) const override {
48 MachineFunctionPass::getAnalysisUsage(AU);
49 }
50
51 bool runOnMachineFunction(MachineFunction &Fn) override;
52
53 private:
54 // Do not instrument leaf functions with this many or fewer instructions. The
55 // shadow call stack instrumented prolog/epilog are slightly race-y reading
56 // and checking the saved return address, so it is better to not instrument
57 // functions that have fewer instructions than the instrumented prolog/epilog
58 // race.
59 static const size_t SkipLeafInstructions = 3;
60 };
61
62 char ShadowCallStack::ID = 0;
63 } // end anonymous namespace.
64
65 static void addProlog(MachineFunction &Fn, const TargetInstrInfo *TII,
66 MachineBasicBlock &MBB, const DebugLoc &DL);
67 static void addPrologLeaf(MachineFunction &Fn, const TargetInstrInfo *TII,
68 MachineBasicBlock &MBB, const DebugLoc &DL,
69 MCPhysReg FreeRegister);
70
71 static void addEpilog(const TargetInstrInfo *TII, MachineBasicBlock &MBB,
72 MachineInstr &MI, MachineBasicBlock &TrapBB);
73 static void addEpilogLeaf(const TargetInstrInfo *TII, MachineBasicBlock &MBB,
74 MachineInstr &MI, MachineBasicBlock &TrapBB,
75 MCPhysReg FreeRegister);
76 // Generate a longer epilog that only uses r10 when a tailcall branches to r11.
77 static void addEpilogOnlyR10(const TargetInstrInfo *TII, MachineBasicBlock &MBB,
78 MachineInstr &MI, MachineBasicBlock &TrapBB);
79
80 // Helper function to add ModR/M references for [Seg: Reg + Offset] memory
81 // accesses
82 static inline const MachineInstrBuilder &
83 addSegmentedMem(const MachineInstrBuilder &MIB, MCPhysReg Seg, MCPhysReg Reg,
84 int Offset = 0) {
85 return MIB.addReg(Reg).addImm(1).addReg(0).addImm(Offset).addReg(Seg);
86 }
87
88 static void addProlog(MachineFunction &Fn, const TargetInstrInfo *TII,
89 MachineBasicBlock &MBB, const DebugLoc &DL) {
90 const MCPhysReg ReturnReg = X86::R10;
91 const MCPhysReg OffsetReg = X86::R11;
92
93 auto MBBI = MBB.begin();
94 // mov r10, [rsp]
95 addDirectMem(BuildMI(MBB, MBBI, DL, TII->get(X86::MOV64rm)).addDef(ReturnReg),
96 X86::RSP);
97 // xor r11, r11
98 BuildMI(MBB, MBBI, DL, TII->get(X86::XOR64rr))
99 .addDef(OffsetReg)
100 .addReg(OffsetReg, RegState::Undef)
101 .addReg(OffsetReg, RegState::Undef);
102 // add QWORD [gs:r11], 8
103 addSegmentedMem(BuildMI(MBB, MBBI, DL, TII->get(X86::ADD64mi8)), X86::GS,
104 OffsetReg)
105 .addImm(8);
106 // mov r11, [gs:r11]
107 addSegmentedMem(
108 BuildMI(MBB, MBBI, DL, TII->get(X86::MOV64rm)).addDef(OffsetReg), X86::GS,
109 OffsetReg);
110 // mov [gs:r11], r10
111 addSegmentedMem(BuildMI(MBB, MBBI, DL, TII->get(X86::MOV64mr)), X86::GS,
112 OffsetReg)
113 .addReg(ReturnReg);
114 }
115
116 static void addPrologLeaf(MachineFunction &Fn, const TargetInstrInfo *TII,
117 MachineBasicBlock &MBB, const DebugLoc &DL,
118 MCPhysReg FreeRegister) {
119 // mov REG, [rsp]
120 addDirectMem(BuildMI(MBB, MBB.begin(), DL, TII->get(X86::MOV64rm))
121 .addDef(FreeRegister),
122 X86::RSP);
123 }
124
125 static void addEpilog(const TargetInstrInfo *TII, MachineBasicBlock &MBB,
126 MachineInstr &MI, MachineBasicBlock &TrapBB) {
127 const DebugLoc &DL = MI.getDebugLoc();
128
129 // xor r11, r11
130 BuildMI(MBB, MI, DL, TII->get(X86::XOR64rr))
131 .addDef(X86::R11)
132 .addReg(X86::R11, RegState::Undef)
133 .addReg(X86::R11, RegState::Undef);
134 // mov r10, [gs:r11]
135 addSegmentedMem(BuildMI(MBB, MI, DL, TII->get(X86::MOV64rm)).addDef(X86::R10),
136 X86::GS, X86::R11);
137 // mov r10, [gs:r10]
138 addSegmentedMem(BuildMI(MBB, MI, DL, TII->get(X86::MOV64rm)).addDef(X86::R10),
139 X86::GS, X86::R10);
140 // sub QWORD [gs:r11], 8
141 // This instruction should not be moved up to avoid a signal race.
142 addSegmentedMem(BuildMI(MBB, MI, DL, TII->get(X86::SUB64mi8)),
143 X86::GS, X86::R11)
144 .addImm(8);
145 // cmp [rsp], r10
146 addDirectMem(BuildMI(MBB, MI, DL, TII->get(X86::CMP64mr)), X86::RSP)
147 .addReg(X86::R10);
148 // jne trap
149 BuildMI(MBB, MI, DL, TII->get(X86::JNE_1)).addMBB(&TrapBB);
150 MBB.addSuccessor(&TrapBB);
151 }
152
153 static void addEpilogLeaf(const TargetInstrInfo *TII, MachineBasicBlock &MBB,
154 MachineInstr &MI, MachineBasicBlock &TrapBB,
155 MCPhysReg FreeRegister) {
156 const DebugLoc &DL = MI.getDebugLoc();
157
158 // cmp [rsp], REG
159 addDirectMem(BuildMI(MBB, MI, DL, TII->get(X86::CMP64mr)), X86::RSP)
160 .addReg(FreeRegister);
161 // jne trap
162 BuildMI(MBB, MI, DL, TII->get(X86::JNE_1)).addMBB(&TrapBB);
163 MBB.addSuccessor(&TrapBB);
164 }
165
166 static void addEpilogOnlyR10(const TargetInstrInfo *TII, MachineBasicBlock &MBB,
167 MachineInstr &MI, MachineBasicBlock &TrapBB) {
168 const DebugLoc &DL = MI.getDebugLoc();
169
170 // xor r10, r10
171 BuildMI(MBB, MI, DL, TII->get(X86::XOR64rr))
172 .addDef(X86::R10)
173 .addReg(X86::R10, RegState::Undef)
174 .addReg(X86::R10, RegState::Undef);
175 // mov r10, [gs:r10]
176 addSegmentedMem(BuildMI(MBB, MI, DL, TII->get(X86::MOV64rm)).addDef(X86::R10),
177 X86::GS, X86::R10);
178 // mov r10, [gs:r10]
179 addSegmentedMem(BuildMI(MBB, MI, DL, TII->get(X86::MOV64rm)).addDef(X86::R10),
180 X86::GS, X86::R10);
181 // sub QWORD [gs:0], 8
182 // This instruction should not be moved up to avoid a signal race.
183 addSegmentedMem(BuildMI(MBB, MI, DL, TII->get(X86::SUB64mi8)), X86::GS, 0)
184 .addImm(8);
185 // cmp [rsp], r10
186 addDirectMem(BuildMI(MBB, MI, DL, TII->get(X86::CMP64mr)), X86::RSP)
187 .addReg(X86::R10);
188 // jne trap
189 BuildMI(MBB, MI, DL, TII->get(X86::JNE_1)).addMBB(&TrapBB);
190 MBB.addSuccessor(&TrapBB);
191 }
192
193 bool ShadowCallStack::runOnMachineFunction(MachineFunction &Fn) {
194 if (!Fn.getFunction().hasFnAttribute(Attribute::ShadowCallStack) ||
195 Fn.getFunction().hasFnAttribute(Attribute::Naked))
196 return false;
197
198 if (Fn.empty() || !Fn.getRegInfo().tracksLiveness())
199 return false;
200
201 // FIXME: Skip functions that have r10 or r11 live on entry (r10 can be live
202 // on entry for parameters with the nest attribute.)
203 if (Fn.front().isLiveIn(X86::R10) || Fn.front().isLiveIn(X86::R11))
204 return false;
205
206 // FIXME: Skip functions with conditional and r10 tail calls for now.
207 bool HasReturn = false;
208 for (auto &MBB : Fn) {
209 if (MBB.empty())
210 continue;
211
212 const MachineInstr &MI = MBB.instr_back();
213 if (MI.isReturn())
214 HasReturn = true;
215
216 if (MI.isReturn() && MI.isCall()) {
217 if (MI.findRegisterUseOperand(X86::EFLAGS))
218 return false;
219 // This should only be possible on Windows 64 (see GR64_TC versus
220 // GR64_TCW64.)
221 if (MI.findRegisterUseOperand(X86::R10) ||
222 MI.hasRegisterImplicitUseOperand(X86::R10))
223 return false;
224 }
225 }
226
227 if (!HasReturn)
228 return false;
229
230 // For leaf functions:
231 // 1. Do not instrument very short functions where it would not improve that
232 // function's security.
233 // 2. Detect if there is an unused caller-saved register we can reserve to
234 // hold the return address instead of writing/reading it from the shadow
235 // call stack.
236 MCPhysReg LeafFuncRegister = X86::NoRegister;
237 if (!Fn.getFrameInfo().adjustsStack()) {
238 size_t InstructionCount = 0;
239 std::bitset UsedRegs;
240 for (auto &MBB : Fn) {
241 for (auto &LiveIn : MBB.liveins())
242 UsedRegs.set(LiveIn.PhysReg);
243 for (auto &MI : MBB) {
244 InstructionCount++;
245 for (auto &Op : MI.operands())
246 if (Op.isReg() && Op.isDef())
247 UsedRegs.set(Op.getReg());
248 }
249 }
250
251 if (InstructionCount <= SkipLeafInstructions)
252 return false;
253
254 std::bitset CalleeSavedRegs;
255 const MCPhysReg *CSRegs = Fn.getRegInfo().getCalleeSavedRegs();
256 for (size_t i = 0; CSRegs[i]; i++)
257 CalleeSavedRegs.set(CSRegs[i]);
258
259 const TargetRegisterInfo *TRI = Fn.getSubtarget().getRegisterInfo();
260 for (auto &Reg : X86::GR64_NOSPRegClass.getRegisters()) {
261 // FIXME: Optimization opportunity: spill/restore a callee-saved register
262 // if a caller-saved register is unavailable.
263 if (CalleeSavedRegs.test(Reg))
264 continue;
265
266 bool Used = false;
267 for (MCSubRegIterator SR(Reg, TRI, true); SR.isValid(); ++SR)
268 if ((Used = UsedRegs.test(*SR)))
269 break;
270
271 if (!Used) {
272 LeafFuncRegister = Reg;
273 break;
274 }
275 }
276 }
277
278 const bool LeafFuncOptimization = LeafFuncRegister != X86::NoRegister;
279 if (LeafFuncOptimization)
280 // Mark the leaf function register live-in for all MBBs except the entry MBB
281 for (auto I = ++Fn.begin(), E = Fn.end(); I != E; ++I)
282 I->addLiveIn(LeafFuncRegister);
283
284 MachineBasicBlock &MBB = Fn.front();
285 const MachineBasicBlock *NonEmpty = MBB.empty() ? MBB.getFallThrough() : &MBB;
286 const DebugLoc &DL = NonEmpty->front().getDebugLoc();
287
288 const TargetInstrInfo *TII = Fn.getSubtarget().getInstrInfo();
289 if (LeafFuncOptimization)
290 addPrologLeaf(Fn, TII, MBB, DL, LeafFuncRegister);
291 else
292 addProlog(Fn, TII, MBB, DL);
293
294 MachineBasicBlock *Trap = nullptr;
295 for (auto &MBB : Fn) {
296 if (MBB.empty())
297 continue;
298
299 MachineInstr &MI = MBB.instr_back();
300 if (MI.isReturn()) {
301 if (!Trap) {
302 Trap = Fn.CreateMachineBasicBlock();
303 BuildMI(Trap, MI.getDebugLoc(), TII->get(X86::TRAP));
304 Fn.push_back(Trap);
305 }
306
307 if (LeafFuncOptimization)
308 addEpilogLeaf(TII, MBB, MI, *Trap, LeafFuncRegister);
309 else if (MI.findRegisterUseOperand(X86::R11))
310 addEpilogOnlyR10(TII, MBB, MI, *Trap);
311 else
312 addEpilog(TII, MBB, MI, *Trap);
313 }
314 }
315
316 return true;
317 }
318
319 INITIALIZE_PASS(ShadowCallStack, "shadow-call-stack", "Shadow Call Stack",
320 false, false)
321
322 FunctionPass *llvm::createShadowCallStackPass() {
323 return new ShadowCallStack();
324 }
4848 /// This pass inserts AVX vzeroupper instructions before each call to avoid
4949 /// transition penalty between functions encoded with AVX and SSE.
5050 FunctionPass *createX86IssueVZeroUpperPass();
51
52 /// This pass instruments the function prolog to save the return address to a
53 /// 'shadow call stack' and the function epilog to check that the return address
54 /// did not change during function execution.
55 FunctionPass *createShadowCallStackPass();
5156
5257 /// This pass inserts ENDBR instructions before indirect jump/call
5358 /// destinations as part of CET IBT mechanism.
5757
5858 void initializeWinEHStatePassPass(PassRegistry &);
5959 void initializeFixupLEAPassPass(PassRegistry &);
60 void initializeShadowCallStackPass(PassRegistry &);
6061 void initializeX86CallFrameOptimizationPass(PassRegistry &);
6162 void initializeX86CmovConverterPassPass(PassRegistry &);
6263 void initializeX86ExecutionDomainFixPass(PassRegistry &);
7677 initializeFixupBWInstPassPass(PR);
7778 initializeEvexToVexInstPassPass(PR);
7879 initializeFixupLEAPassPass(PR);
80 initializeShadowCallStackPass(PR);
7981 initializeX86CallFrameOptimizationPass(PR);
8082 initializeX86CmovConverterPassPass(PR);
8183 initializeX86ExecutionDomainFixPass(PR);
472474 addPass(createBreakFalseDeps());
473475 }
474476
477 addPass(createShadowCallStackPass());
475478 addPass(createX86IndirectBranchTrackingPass());
476479
477480 if (UseVZeroUpper)
4848 ; CHECK-NEXT: Post-RA pseudo instruction expansion pass
4949 ; CHECK-NEXT: X86 pseudo instruction expansion pass
5050 ; CHECK-NEXT: Analyze Machine Code For Garbage Collection
51 ; CHECK-NEXT: Shadow Call Stack
5152 ; CHECK-NEXT: X86 Indirect Branch Tracking
5253 ; CHECK-NEXT: X86 vzeroupper inserter
5354 ; CHECK-NEXT: Contiguously Lay Out Funclets
141141 ; CHECK-NEXT: ReachingDefAnalysis
142142 ; CHECK-NEXT: X86 Execution Dependency Fix
143143 ; CHECK-NEXT: BreakFalseDeps
144 ; CHECK-NEXT: Shadow Call Stack
144145 ; CHECK-NEXT: X86 Indirect Branch Tracking
145146 ; CHECK-NEXT: X86 vzeroupper inserter
146147 ; CHECK-NEXT: MachineDominator Tree Construction
0 # RUN: llc -mtriple=x86_64-unknown-linux-gnu -run-pass shadow-call-stack -verify-machineinstrs -o - %s | FileCheck %s
1 --- |
2
3 define void @no_return() #0 { ret void }
4 define void @normal_return() #0 { ret void }
5 define void @normal_return_leaf_func() #0 { ret void }
6 define void @short_leaf_func() #0 { ret void }
7 define void @normal_tail_call() #0 { ret void }
8 define void @r11_tail_call() #0 { ret void }
9 define void @conditional_tail_call() #0 { ret void }
10 define void @r10_live_in() #0 { ret void }
11
12 attributes #0 = { shadowcallstack }
13
14 ...
15 ---
16 # CHECK-LABEL: name: no_return
17 name: no_return
18 tracksRegLiveness: true
19 frameInfo:
20 adjustsStack: true # not a leaf function
21 body: |
22 ; CHECK: bb.0:
23 bb.0:
24 ; CHECK-NEXT: $eax = MOV32ri 13
25 $eax = MOV32ri 13
26 ...
27 ---
28 # CHECK-LABEL: name: normal_return
29 name: normal_return
30 tracksRegLiveness: true
31 frameInfo:
32 adjustsStack: true # not a leaf function
33 body: |
34 ; CHECK: bb.0:
35 bb.0:
36 ; CHECK: $r10 = MOV64rm $rsp, 1, $noreg, 0, $noreg
37 ; CHECK-NEXT: $r11 = XOR64rr undef $r11, undef $r11, implicit-def $eflags
38 ; CHECK-NEXT: ADD64mi8 $r11, 1, $noreg, 0, $gs, 8, implicit-def $eflags
39 ; CHECK-NEXT: $r11 = MOV64rm $r11, 1, $noreg, 0, $gs
40 ; CHECK-NEXT: MOV64mr $r11, 1, $noreg, 0, $gs, $r10
41 ; CHECK-NEXT: $eax = MOV32ri 13
42 $eax = MOV32ri 13
43
44 ; CHECK-NEXT: $r11 = XOR64rr undef $r11, undef $r11, implicit-def $eflags
45 ; CHECK-NEXT: $r10 = MOV64rm $r11, 1, $noreg, 0, $gs
46 ; CHECK-NEXT: $r10 = MOV64rm $r10, 1, $noreg, 0, $gs
47 ; CHECK-NEXT: SUB64mi8 $r11, 1, $noreg, 0, $gs, 8, implicit-def $eflags
48 ; CHECK-NEXT: CMP64mr $rsp, 1, $noreg, 0, $noreg, $r10, implicit-def $eflags
49 ; CHECK-NEXT: JNE_1 %bb.1, implicit $eflags
50 ; CHECK-NEXT: RETQ $eax
51 RETQ $eax
52
53 ; CHECK: bb.1:
54 ; CHECK-NEXT; TRAP
55 ...
56 ---
57 # CHECK-LABEL: name: normal_return_leaf_func
58 name: normal_return_leaf_func
59 tracksRegLiveness: true
60 frameInfo:
61 adjustsStack: false # leaf function
62 body: |
63 ; CHECK: bb.0:
64 ; CHECK: liveins: $rcx
65 bb.0:
66 liveins: $rcx
67
68 ; CHECK: $rdx = MOV64rm $rsp, 1, $noreg, 0, $noreg
69 ; CHECK-NEXT: $eax = MOV32ri 0
70 $eax = MOV32ri 0
71 ; CHECK-NEXT: CMP64ri8 $rcx, 5, implicit-def $eflags
72 CMP64ri8 $rcx, 5, implicit-def $eflags
73 ; CHECK-NEXT: JA_1 %bb.1, implicit $eflags
74 JA_1 %bb.1, implicit $eflags
75 ; CHECK-NEXT: JMP_1 %bb.2
76 JMP_1 %bb.2
77
78 ; CHECK: bb.1
79 ; CHECK: liveins: $eax, $rdx
80 bb.1:
81 liveins: $eax
82
83 ; CHECKT: $eax = MOV32ri 1
84 $eax = MOV32ri 1
85
86 ; CHECK: bb.2
87 ; CHECK: liveins: $eax, $rdx
88 bb.2:
89 liveins: $eax
90
91 ; CHECK: CMP64mr $rsp, 1, $noreg, 0, $noreg, $rdx, implicit-def $eflags
92 ; CHECK-NEXT: JNE_1 %bb.3, implicit $eflags
93 ; CHECK-NEXT: RETQ $eax
94 RETQ $eax
95
96 ; CHECK: bb.3:
97 ; CHECK-NEXT; TRAP
98 ...
99 ---
100 # CHECK-LABEL: name: short_leaf_func
101 name: short_leaf_func
102 tracksRegLiveness: true
103 frameInfo:
104 adjustsStack: false # leaf function
105 body: |
106 ; CHECK: bb.0:
107 bb.0:
108 ; CHECK: $eax = MOV32ri 13
109 $eax = MOV32ri 13
110
111 ; CHECK-NEXT: RETQ $eax
112 RETQ $eax
113 ...
114 ---
115 # CHECK-LABEL: name: normal_tail_call
116 name: normal_tail_call
117 tracksRegLiveness: true
118 frameInfo:
119 adjustsStack: true # not a leaf function
120 body: |
121 ; CHECK: bb.0:
122 bb.0:
123 ; CHECK: $r10 = MOV64rm $rsp, 1, $noreg, 0, $noreg
124 ; CHECK-NEXT: $r11 = XOR64rr undef $r11, undef $r11, implicit-def $eflags
125 ; CHECK-NEXT: ADD64mi8 $r11, 1, $noreg, 0, $gs, 8, implicit-def $eflags
126 ; CHECK-NEXT: $r11 = MOV64rm $r11, 1, $noreg, 0, $gs
127 ; CHECK-NEXT: MOV64mr $r11, 1, $noreg, 0, $gs, $r10
128 ; CHECK-NEXT: $eax = MOV32ri 13
129 $eax = MOV32ri 13
130
131 ; CHECK-NEXT: $r11 = XOR64rr undef $r11, undef $r11, implicit-def $eflags
132 ; CHECK-NEXT: $r10 = MOV64rm $r11, 1, $noreg, 0, $gs
133 ; CHECK-NEXT: $r10 = MOV64rm $r10, 1, $noreg, 0, $gs
134 ; CHECK-NEXT: SUB64mi8 $r11, 1, $noreg, 0, $gs, 8, implicit-def $eflags
135 ; CHECK-NEXT: CMP64mr $rsp, 1, $noreg, 0, $noreg, $r10, implicit-def $eflags
136 ; CHECK-NEXT: JNE_1 %bb.1, implicit $eflags
137 ; CHECK-NEXT: TAILJMPr64 $rax
138 TAILJMPr64 $rax
139
140 ; CHECK: bb.1:
141 ; CHECK-NEXT; TRAP
142 ...
143 ---
144 # CHECK-LABEL: name: r11_tail_call
145 name: r11_tail_call
146 tracksRegLiveness: true
147 frameInfo:
148 adjustsStack: true # not a leaf function
149 body: |
150 ; CHECK: bb.0:
151 bb.0:
152 ; CHECK: $r10 = MOV64rm $rsp, 1, $noreg, 0, $noreg
153 ; CHECK-NEXT: $r11 = XOR64rr undef $r11, undef $r11, implicit-def $eflags
154 ; CHECK-NEXT: ADD64mi8 $r11, 1, $noreg, 0, $gs, 8, implicit-def $eflags
155 ; CHECK-NEXT: $r11 = MOV64rm $r11, 1, $noreg, 0, $gs
156 ; CHECK-NEXT: MOV64mr $r11, 1, $noreg, 0, $gs, $r10
157 ; CHECK-NEXT: $eax = MOV32ri 13
158 $eax = MOV32ri 13
159
160 ; CHECK-NEXT: $r10 = XOR64rr undef $r10, undef $r10, implicit-def $eflags
161 ; CHECK-NEXT: $r10 = MOV64rm $r10, 1, $noreg, 0, $gs
162 ; CHECK-NEXT: $r10 = MOV64rm $r10, 1, $noreg, 0, $gs
163 ; CHECK-NEXT: SUB64mi8 $noreg, 1, $noreg, 0, $gs, 8, implicit-def $eflags
164 ; CHECK-NEXT: CMP64mr $rsp, 1, $noreg, 0, $noreg, $r10, implicit-def $eflags
165 ; CHECK-NEXT: JNE_1 %bb.1, implicit $eflags
166 ; CHECK-NEXT: TAILJMPr64 undef $r11
167 TAILJMPr64 undef $r11
168
169 ; CHECK: bb.1:
170 ; CHECK-NEXT; TRAP
171 ...
172 ---
173 # CHECK-LABEL: name: conditional_tail_call
174 name: conditional_tail_call
175 tracksRegLiveness: true
176 frameInfo:
177 adjustsStack: true # not a leaf function
178 body: |
179 ; CHECK: bb.0:
180 bb.0:
181 ; CHECK: $eax = MOV32ri 13
182 $eax = MOV32ri 13
183
184 ; CHECK-NEXT: TAILJMPd64_CC @conditional_tail_call, undef $eflags
185 TAILJMPd64_CC @conditional_tail_call, undef $eflags
186 ...
187 ---
188 # CHECK-LABEL: name: r10_live_in
189 name: r10_live_in
190 tracksRegLiveness: true
191 frameInfo:
192 adjustsStack: true # not a leaf function
193 body: |
194 ; CHECK: bb.0:
195 ; CHECK: liveins: $r10
196 bb.0:
197 liveins: $r10
198
199 ; CHECK: $eax = MOV32ri 13
200 $eax = MOV32ri 13
201 ; CHECK-NEXT: RETQ $eax
202 RETQ $eax
203 ...