llvm.org GIT mirror llvm / 165d46d
[PartialInlining] Support shrinkwrap life_range markers Differential Revision: http://reviews.llvm.org/D33847 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@305170 91177308-0d34-0410-b5e6-96231b3b80d8 Xinliang David Li 2 years ago
7 changed file(s) with 581 addition(s) and 18 deletion(s). Raw diff Collapse all Expand all
105105 /// significant impact on the cost however.
106106 void findInputsOutputs(ValueSet &Inputs, ValueSet &Outputs,
107107 const ValueSet &Allocas) const;
108
109 /// Check if life time marker nodes can be hoisted/sunk into the outline
110 /// region.
111 ///
112 /// Returns true if it is safe to do the code motion.
113 bool isLegalToShrinkwrapLifetimeMarkers(Instruction *AllocaAddr) const;
108114 /// Find the set of allocas whose life ranges are contained within the
109115 /// outlined region.
110116 ///
111117 /// Allocas which have life_time markers contained in the outlined region
112118 /// should be pushed to the outlined function. The address bitcasts that
113119 /// are used by the lifetime markers are also candidates for shrink-
114 /// wrapping. The instructions that need to be sinked are collected in
120 /// wrapping. The instructions that need to be sunk are collected in
115121 /// 'Allocas'.
116 void findAllocas(ValueSet &Allocas) const;
122 void findAllocas(ValueSet &SinkCands, ValueSet &HoistCands,
123 BasicBlock *&ExitBlock) const;
124
125 /// Find or create a block within the outline region for placing hoisted
126 /// code.
127 ///
128 /// CommonExitBlock is block outside the outline region. It is the common
129 /// successor of blocks inside the region. If there exists a single block
130 /// inside the region that is the predecessor of CommonExitBlock, that block
131 /// will be returned. Otherwise CommonExitBlock will be split and the
132 /// original block will be added to the outline region.
133 BasicBlock *findOrCreateBlockForHoisting(BasicBlock *CommonExitBlock);
117134
118135 private:
119136 void severSplitPHINodes(BasicBlock *&Header);
141141 return false;
142142 }
143143
144 void CodeExtractor::findAllocas(ValueSet &SinkCands) const {
144 static BasicBlock *getCommonExitBlock(const SetVector &Blocks) {
145 BasicBlock *CommonExitBlock = nullptr;
146 auto hasNonCommonExitSucc = [&](BasicBlock *Block) {
147 for (auto *Succ : successors(Block)) {
148 // Internal edges, ok.
149 if (Blocks.count(Succ))
150 continue;
151 if (!CommonExitBlock) {
152 CommonExitBlock = Succ;
153 continue;
154 }
155 if (CommonExitBlock == Succ)
156 continue;
157
158 return true;
159 }
160 return false;
161 };
162
163 if (any_of(Blocks, hasNonCommonExitSucc))
164 return nullptr;
165
166 return CommonExitBlock;
167 }
168
169 bool CodeExtractor::isLegalToShrinkwrapLifetimeMarkers(
170 Instruction *Addr) const {
171 AllocaInst *AI = cast(Addr->stripInBoundsConstantOffsets());
145172 Function *Func = (*Blocks.begin())->getParent();
173 for (BasicBlock &BB : *Func) {
174 if (Blocks.count(&BB))
175 continue;
176 for (Instruction &II : BB) {
177
178 if (isa(II))
179 continue;
180
181 unsigned Opcode = II.getOpcode();
182 Value *MemAddr = nullptr;
183 switch (Opcode) {
184 case Instruction::Store:
185 case Instruction::Load: {
186 if (Opcode == Instruction::Store) {
187 StoreInst *SI = cast(&II);
188 MemAddr = SI->getPointerOperand();
189 } else {
190 LoadInst *LI = cast(&II);
191 MemAddr = LI->getPointerOperand();
192 }
193 // Global variable can not be aliased with locals.
194 if (dyn_cast(MemAddr))
195 break;
196 Value *Base = MemAddr->stripInBoundsConstantOffsets();
197 if (!dyn_cast(Base) || Base == AI)
198 return false;
199 break;
200 }
201 default: {
202 IntrinsicInst *IntrInst = dyn_cast(&II);
203 if (IntrInst) {
204 if (IntrInst->getIntrinsicID() == Intrinsic::lifetime_start ||
205 IntrInst->getIntrinsicID() == Intrinsic::lifetime_end)
206 break;
207 return false;
208 }
209 // Treat all the other cases conservatively if it has side effects.
210 if (II.mayHaveSideEffects())
211 return false;
212 }
213 }
214 }
215 }
216
217 return true;
218 }
219
220 BasicBlock *
221 CodeExtractor::findOrCreateBlockForHoisting(BasicBlock *CommonExitBlock) {
222 BasicBlock *SinglePredFromOutlineRegion = nullptr;
223 assert(!Blocks.count(CommonExitBlock) &&
224 "Expect a block outside the region!");
225 for (auto *Pred : predecessors(CommonExitBlock)) {
226 if (!Blocks.count(Pred))
227 continue;
228 if (!SinglePredFromOutlineRegion) {
229 SinglePredFromOutlineRegion = Pred;
230 } else if (SinglePredFromOutlineRegion != Pred) {
231 SinglePredFromOutlineRegion = nullptr;
232 break;
233 }
234 }
235
236 if (SinglePredFromOutlineRegion)
237 return SinglePredFromOutlineRegion;
238
239 #ifndef NDEBUG
240 auto getFirstPHI = [](BasicBlock *BB) {
241 BasicBlock::iterator I = BB->begin();
242 PHINode *FirstPhi = nullptr;
243 while (I != BB->end()) {
244 PHINode *Phi = dyn_cast(I);
245 if (!Phi)
246 break;
247 if (!FirstPhi) {
248 FirstPhi = Phi;
249 break;
250 }
251 }
252 return FirstPhi;
253 };
254 // If there are any phi nodes, the single pred either exists or has already
255 // be created before code extraction.
256 assert(!getFirstPHI(CommonExitBlock) && "Phi not expected");
257 #endif
258
259 BasicBlock *NewExitBlock = CommonExitBlock->splitBasicBlock(
260 CommonExitBlock->getFirstNonPHI()->getIterator());
261
262 for (auto *Pred : predecessors(CommonExitBlock)) {
263 if (Blocks.count(Pred))
264 continue;
265 Pred->getTerminator()->replaceUsesOfWith(CommonExitBlock, NewExitBlock);
266 }
267 // Now add the old exit block to the outline region.
268 Blocks.insert(CommonExitBlock);
269 return CommonExitBlock;
270 }
271
272 void CodeExtractor::findAllocas(ValueSet &SinkCands, ValueSet &HoistCands,
273 BasicBlock *&ExitBlock) const {
274 Function *Func = (*Blocks.begin())->getParent();
275 ExitBlock = getCommonExitBlock(Blocks);
276
146277 for (BasicBlock &BB : *Func) {
147278 if (Blocks.count(&BB))
148279 continue;
151282 if (!AI)
152283 continue;
153284
154 // Returns true if matching life time markers are found within
155 // the outlined region.
156 auto GetLifeTimeMarkers = [&](Instruction *Addr) {
285 // Find the pair of life time markers for address 'Addr' that are either
286 // defined inside the outline region or can legally be shrinkwrapped into
287 // the outline region. If there are not other untracked uses of the
288 // address, return the pair of markers if found; otherwise return a pair
289 // of nullptr.
290 auto GetLifeTimeMarkers =
291 [&](Instruction *Addr, bool &SinkLifeStart,
292 bool &HoistLifeEnd) -> std::pair {
157293 Instruction *LifeStart = nullptr, *LifeEnd = nullptr;
294
158295 for (User *U : Addr->users()) {
159 if (!definedInRegion(Blocks, U))
160 return false;
161
162296 IntrinsicInst *IntrInst = dyn_cast(U);
163297 if (IntrInst) {
164 if (IntrInst->getIntrinsicID() == Intrinsic::lifetime_start)
298 if (IntrInst->getIntrinsicID() == Intrinsic::lifetime_start) {
299 // Do not handle the case where AI has multiple start markers.
300 if (LifeStart)
301 return std::make_pair(nullptr, nullptr);
165302 LifeStart = IntrInst;
166 if (IntrInst->getIntrinsicID() == Intrinsic::lifetime_end)
303 }
304 if (IntrInst->getIntrinsicID() == Intrinsic::lifetime_end) {
305 if (LifeEnd)
306 return std::make_pair(nullptr, nullptr);
167307 LifeEnd = IntrInst;
308 }
309 continue;
168310 }
311 // Find untracked uses of the address, bail.
312 if (!definedInRegion(Blocks, U))
313 return std::make_pair(nullptr, nullptr);
169314 }
170 return LifeStart && LifeEnd;
315
316 if (!LifeStart || !LifeEnd)
317 return std::make_pair(nullptr, nullptr);
318
319 SinkLifeStart = !definedInRegion(Blocks, LifeStart);
320 HoistLifeEnd = !definedInRegion(Blocks, LifeEnd);
321 // Do legality Check.
322 if ((SinkLifeStart || HoistLifeEnd) &&
323 !isLegalToShrinkwrapLifetimeMarkers(Addr))
324 return std::make_pair(nullptr, nullptr);
325
326 // Check to see if we have a place to do hoisting, if not, bail.
327 if (HoistLifeEnd && !ExitBlock)
328 return std::make_pair(nullptr, nullptr);
329
330 return std::make_pair(LifeStart, LifeEnd);
171331 };
172332
173 if (GetLifeTimeMarkers(AI)) {
333 bool SinkLifeStart = false, HoistLifeEnd = false;
334 auto Markers = GetLifeTimeMarkers(AI, SinkLifeStart, HoistLifeEnd);
335
336 if (Markers.first) {
337 if (SinkLifeStart)
338 SinkCands.insert(Markers.first);
174339 SinkCands.insert(AI);
340 if (HoistLifeEnd)
341 HoistCands.insert(Markers.second);
175342 continue;
176343 }
177344
178 // Follow the bitcast:
345 // Follow the bitcast.
179346 Instruction *MarkerAddr = nullptr;
180347 for (User *U : AI->users()) {
181 if (U->stripPointerCasts() == AI) {
348
349 if (U->stripInBoundsConstantOffsets() == AI) {
350 SinkLifeStart = false;
351 HoistLifeEnd = false;
182352 Instruction *Bitcast = cast(U);
183 if (GetLifeTimeMarkers(Bitcast)) {
353 Markers = GetLifeTimeMarkers(Bitcast, SinkLifeStart, HoistLifeEnd);
354 if (Markers.first) {
184355 MarkerAddr = Bitcast;
185356 continue;
186357 }
187358 }
359
360 // Found unknown use of AI.
188361 if (!definedInRegion(Blocks, U)) {
189362 MarkerAddr = nullptr;
190363 break;
191364 }
192365 }
366
193367 if (MarkerAddr) {
368 if (SinkLifeStart)
369 SinkCands.insert(Markers.first);
194370 if (!definedInRegion(Blocks, MarkerAddr))
195371 SinkCands.insert(MarkerAddr);
196372 SinkCands.insert(AI);
373 if (HoistLifeEnd)
374 HoistCands.insert(Markers.second);
197375 }
198376 }
199377 }
779957 if (!isEligible())
780958 return nullptr;
781959
782 ValueSet inputs, outputs, SinkingCands;
960 ValueSet inputs, outputs, SinkingCands, HoistingCands;
961 BasicBlock *CommonExit = nullptr;
783962
784963 // Assumption: this is a single-entry code region, and the header is the first
785964 // block in the region.
818997 "newFuncRoot");
819998 newFuncRoot->getInstList().push_back(BranchInst::Create(header));
820999
821 findAllocas(SinkingCands);
1000 findAllocas(SinkingCands, HoistingCands, CommonExit);
1001 assert(HoistingCands.empty() || CommonExit);
8221002
8231003 // Find inputs to, outputs from the code region.
8241004 findInputsOutputs(inputs, outputs, SinkingCands);
8271007 for (auto *II : SinkingCands)
8281008 cast(II)->moveBefore(*newFuncRoot,
8291009 newFuncRoot->getFirstInsertionPt());
1010
1011 if (!HoistingCands.empty()) {
1012 auto *HoistToBlock = findOrCreateBlockForHoisting(CommonExit);
1013 Instruction *TI = HoistToBlock->getTerminator();
1014 for (auto *II : HoistingCands)
1015 cast(II)->moveBefore(TI);
1016 }
8301017
8311018 // Calculate the exit blocks for the extracted region and the total exit
8321019 // weights for each of those blocks.
0 ; RUN: opt -S -partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
1 ; RUN: opt -S -passes=partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
2
3 %class.A = type { i32 }
4 @cond = local_unnamed_addr global i32 0, align 4
5
6 ; Function Attrs: uwtable
7 define void @_Z3foov() local_unnamed_addr {
8 bb:
9 %tmp = alloca %class.A, align 4
10 %tmp1 = bitcast %class.A* %tmp to i8*
11 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp1)
12 %tmp2 = load i32, i32* @cond, align 4, !tbaa !2
13 %tmp3 = icmp eq i32 %tmp2, 0
14 br i1 %tmp3, label %bb4, label %bb5
15
16 bb4: ; preds = %bb
17 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
18 br label %bb5
19
20 bb5: ; preds = %bb4, %bb
21 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp1)
22 ret void
23 }
24
25 ; Function Attrs: argmemonly nounwind
26 declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
27
28 declare void @_ZN1A7memfuncEv(%class.A*) local_unnamed_addr
29
30 ; Function Attrs: argmemonly nounwind
31 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
32
33 ; Function Attrs: uwtable
34 define void @_Z3goov() local_unnamed_addr {
35 ; CHECK-LABEL: @_Z3goov()
36 bb:
37 ; CHECK: bb:
38 ; CHECK-NOT: alloca
39 ; CHECK-NOT: bitcast
40 ; CHECK-NOT: llvm.lifetime
41 ; CHECK: br i1
42 ; CHECK: codeRepl.i:
43 ; CHECK: call void @_Z3foov.1_
44
45 tail call void @_Z3foov()
46 ret void
47 }
48
49 ; CHECK-LABEL: define internal void @_Z3foov.1_
50 ; CHECK: newFuncRoot:
51 ; CHECK-NEXT: %tmp = alloca %class.A
52 ; CHECK-NEXT: %tmp1 = bitcast %class.A* %tmp to i8*
53 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp1)
54 ; CHECK: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp1)
55 ; CHECK-NEXT: br label %bb5.exitStub
56
57
58 !llvm.module.flags = !{!0}
59 !llvm.ident = !{!1}
60
61 !0 = !{i32 1, !"wchar_size", i32 4}
62 !1 = !{!"clang version 5.0.0 (trunk 304489)"}
63 !2 = !{!3, !3, i64 0}
64 !3 = !{!"int", !4, i64 0}
65 !4 = !{!"omnipotent char", !5, i64 0}
66 !5 = !{!"Simple C++ TBAA"}
0 ; RUN: opt -S -partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
1 ; RUN: opt -S -passes=partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
2
3 %class.A = type { i8 }
4
5 @cond = local_unnamed_addr global i32 0, align 4
6
7 ; Function Attrs: uwtable
8 define void @_Z3foov() local_unnamed_addr {
9 bb:
10 %tmp = alloca %class.A, align 1
11 %tmp1 = getelementptr inbounds %class.A, %class.A* %tmp, i64 0, i32 0
12 call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %tmp1)
13 %tmp2 = load i32, i32* @cond, align 4, !tbaa !2
14 %tmp3 = icmp eq i32 %tmp2, 0
15 br i1 %tmp3, label %bb4, label %bb5
16
17 bb4: ; preds = %bb
18 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
19 br label %bb5
20
21 bb5: ; preds = %bb4, %bb
22 call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %tmp1)
23 ret void
24 }
25
26 ; Function Attrs: argmemonly nounwind
27 declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
28
29 declare void @_ZN1A7memfuncEv(%class.A*) local_unnamed_addr
30
31 ; Function Attrs: argmemonly nounwind
32 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
33
34 ; Function Attrs: uwtable
35 define void @_Z3goov() local_unnamed_addr {
36 ; CHECK-LABEL: @_Z3goov()
37 bb:
38 ; CHECK: bb:
39 ; CHECK-NOT: alloca
40 ; CHECK-NOT: getelementptr
41 ; CHECK-NOT: llvm.lifetime
42 ; CHECK: br i1
43 ; CHECK: codeRepl.i:
44 ; CHECK: call void @_Z3foov.1_
45 tail call void @_Z3foov()
46 ret void
47 }
48
49 ; CHECK-LABEL: define internal void @_Z3foov.1_
50 ; CHECK: newFuncRoot:
51 ; CHECK-NEXT: %tmp = alloca %class.A
52 ; CHECK-NEXT: %tmp1 = getelementptr
53 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8
54 ; CHECK: call void @llvm.lifetime.end.p0i8
55 ; CHECK-NEXT: br label %bb5.exitStub
56
57 !llvm.module.flags = !{!0}
58 !llvm.ident = !{!1}
59
60 !0 = !{i32 1, !"wchar_size", i32 4}
61 !1 = !{!"clang version 5.0.0 (trunk 304489)"}
62 !2 = !{!3, !3, i64 0}
63 !3 = !{!"int", !4, i64 0}
64 !4 = !{!"omnipotent char", !5, i64 0}
65 !5 = !{!"Simple C++ TBAA"}
0 ; RUN: opt -S -partial-inliner -max-num-inline-blocks=2 -skip-partial-inlining-cost-analysis < %s | FileCheck %s
1 ; RUN: opt -S -passes=partial-inliner -max-num-inline-blocks=2 -skip-partial-inlining-cost-analysis < %s | FileCheck %s
2
3 %class.A = type { i32 }
4
5 @cond = local_unnamed_addr global i32 0, align 4
6
7 ; Function Attrs: uwtable
8 define void @_Z3foov() local_unnamed_addr {
9 bb:
10 %tmp = alloca %class.A, align 4
11 %tmp1 = bitcast %class.A* %tmp to i8*
12 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp1)
13 %tmp2 = load i32, i32* @cond, align 4, !tbaa !2
14 %tmp3 = icmp eq i32 %tmp2, 0
15 br i1 %tmp3, label %bb4, label %bb9
16
17 bb4: ; preds = %bb
18 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
19 %tmp5 = getelementptr inbounds %class.A, %class.A* %tmp, i64 0, i32 0
20 %tmp6 = load i32, i32* %tmp5, align 4, !tbaa !6
21 %tmp7 = icmp sgt i32 %tmp6, 0
22 br i1 %tmp7, label %bb9, label %bb8
23
24 bb8: ; preds = %bb4
25 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
26 br label %bb9
27
28 bb9: ; preds = %bb8, %bb4, %bb
29 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp1)
30 ret void
31 }
32
33 ; Function Attrs: argmemonly nounwind
34 declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
35
36 declare void @_ZN1A7memfuncEv(%class.A*) local_unnamed_addr
37
38 ; Function Attrs: argmemonly nounwind
39 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
40
41 ; Function Attrs: uwtable
42 define void @_Z3goov() local_unnamed_addr {
43 bb:
44 tail call void @_Z3foov()
45 ret void
46 }
47
48 ; CHECK-LABEL: define internal void @_Z3foov.1_
49 ; CHECK: bb9:
50 ; CHECK: call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp1)
51 ; CHECK: br label %.exitStub
52
53
54
55 !llvm.module.flags = !{!0}
56 !llvm.ident = !{!1}
57
58 !0 = !{i32 1, !"wchar_size", i32 4}
59 !1 = !{!"clang version 5.0.0 (trunk 304489)"}
60 !2 = !{!3, !3, i64 0}
61 !3 = !{!"int", !4, i64 0}
62 !4 = !{!"omnipotent char", !5, i64 0}
63 !5 = !{!"Simple C++ TBAA"}
64 !6 = !{!7, !3, i64 0}
65 !7 = !{!"_ZTS1A", !3, i64 0}
0 ; RUN: opt -S -partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
1 ; RUN: opt -S -passes=partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
2
3 %class.A = type { i32 }
4 @cond = local_unnamed_addr global i32 0, align 4
5
6 ; Function Attrs: uwtable
7 define void @_Z3foov() local_unnamed_addr {
8 bb:
9 %tmp = alloca %class.A, align 4
10 %tmp1 = alloca %class.A, align 4
11 %tmp2 = bitcast %class.A* %tmp to i8*
12 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp2)
13 %tmp3 = bitcast %class.A* %tmp1 to i8*
14 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp3)
15 %tmp4 = load i32, i32* @cond, align 4, !tbaa !2
16 %tmp5 = icmp eq i32 %tmp4, 0
17 br i1 %tmp5, label %bb6, label %bb7
18
19 bb6: ; preds = %bb
20 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
21 br label %bb7
22
23 bb7: ; preds = %bb6, %bb
24 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp3)
25 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp2)
26 ret void
27 }
28
29 ; Function Attrs: argmemonly nounwind
30 declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
31
32 declare void @_ZN1A7memfuncEv(%class.A*) local_unnamed_addr
33
34 ; Function Attrs: argmemonly nounwind
35 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
36
37 ; Function Attrs: uwtable
38 define void @_Z3goov() local_unnamed_addr {
39 bb:
40 tail call void @_Z3foov()
41 ret void
42 }
43
44 ; CHECK-LABEL: define internal void @_Z3foov.1_
45 ; CHECK: newFuncRoot:
46 ; CHECK-NEXT: alloca
47 ; CHECK-NEXT: bitcast
48 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8
49 ; CHECK-NEXT: alloca
50 ; CHECK-NEXT: bitcast
51 ; CHECK-NEXT: call void @llvm.lifetime.start.p0i8
52 ; CHECK: call void @llvm.lifetime.end.p0i8
53 ; CHECK-NEXT: call void @llvm.lifetime.end.p0i8
54 ; CHECK-NEXT: br label {{.*}}exitStub
55
56
57 !llvm.module.flags = !{!0}
58 !llvm.ident = !{!1}
59
60 !0 = !{i32 1, !"wchar_size", i32 4}
61 !1 = !{!"clang version 5.0.0 (trunk 304489)"}
62 !2 = !{!3, !3, i64 0}
63 !3 = !{!"int", !4, i64 0}
64 !4 = !{!"omnipotent char", !5, i64 0}
65 !5 = !{!"Simple C++ TBAA"}
0 ; The expected behavior of this file is expected to change when partial
1 ; inlining legality check is enhanced.
2
3 ; RUN: opt -S -partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
4 ; RUN: opt -S -passes=partial-inliner -skip-partial-inlining-cost-analysis < %s | FileCheck %s
5
6 %class.A = type { i32 }
7
8 @cond = local_unnamed_addr global i32 0, align 4
9 @condptr = external local_unnamed_addr global i32*, align 8
10
11 ; Function Attrs: uwtable
12 define void @_Z3foo_unknown_mem_accessv() local_unnamed_addr {
13 bb:
14 %tmp = alloca %class.A, align 4
15 %tmp1 = alloca %class.A, align 4
16 %tmp2 = bitcast %class.A* %tmp to i8*
17 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp2)
18 %tmp3 = bitcast %class.A* %tmp1 to i8*
19 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp3)
20 %tmp4 = load i32*, i32** @condptr, align 8, !tbaa !2
21 %tmp5 = load i32, i32* %tmp4, align 4, !tbaa !6
22 %tmp6 = icmp eq i32 %tmp5, 0
23 br i1 %tmp6, label %bb7, label %bb8
24
25 bb7: ; preds = %bb
26 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
27 br label %bb8
28
29 bb8: ; preds = %bb7, %bb
30 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp3)
31 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp2)
32 ret void
33 }
34
35 declare void @_Z3barv() local_unnamed_addr
36 declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
37 declare void @_ZN1A7memfuncEv(%class.A*) local_unnamed_addr
38 declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)
39
40 define void @_Z3foo_unknown_calli(i32 %arg) local_unnamed_addr {
41 bb:
42 %tmp = alloca %class.A, align 4
43 %tmp1 = bitcast %class.A* %tmp to i8*
44 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp1)
45 tail call void @_Z3barv()
46 %tmp2 = icmp eq i32 %arg, 0
47 br i1 %tmp2, label %bb3, label %bb4
48
49 bb3: ; preds = %bb
50 call void @_ZN1A7memfuncEv(%class.A* nonnull %tmp)
51 br label %bb4
52
53 bb4: ; preds = %bb3, %bb
54 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %tmp1)
55 ret void
56 }
57
58 define void @_Z3goov() local_unnamed_addr {
59 ; CHECK-LABEL: @_Z3goov
60 ; CHECK-NEXT: bb:
61 ; CHECK: alloca
62 ; CHECK: lifetime
63 bb:
64 call void @_Z3foo_unknown_mem_accessv()
65 %tmp = load i32, i32* @cond, align 4, !tbaa !2
66 tail call void @_Z3foo_unknown_calli(i32 %tmp)
67 ret void
68 }
69
70 ; CHECK-LABEL define internal void @_Z3foo_unknown_calli.1_bb3
71 ; CHECK: newFuncRoot:
72 ; CHECK-NEXT: br label %bb3
73
74 ; CHECK: bb4.exitStub:
75 ; CHECK-NEXT: ret void
76
77 ; CHECK: bb3:
78 ; CHECK-NOT: lifetime.ed
79 ; CHECK: br label %bb4.exitStub
80
81
82
83 !llvm.module.flags = !{!0}
84 !llvm.ident = !{!1}
85
86 !0 = !{i32 1, !"wchar_size", i32 4}
87 !1 = !{!"clang version 5.0.0 (trunk 304489)"}
88 !2 = !{!3, !3, i64 0}
89 !3 = !{!"any pointer", !4, i64 0}
90 !4 = !{!"omnipotent char", !5, i64 0}
91 !5 = !{!"Simple C++ TBAA"}
92 !6 = !{!7, !7, i64 0}
93 !7 = !{!"int", !4, i64 0}