Skip to content

Commit ee9a3eb

Browse files
committed
[CodeGenPrepare] Handle ExtractValueInst in dupRetToEnableTailCallOpts
As the test case shows if there is an ExtractValueInst in the Ret block, function dupRetToEnableTailCallOpts can't duplicate it into the block containing call. So later no tail call is generated in CodeGen. This patch adds the ExtractValueInst handling code in function dupRetToEnableTailCallOpts and FoldReturnIntoUncondBranch, and later tail call can be generated for this case. Differential Revision: https://reviews.llvm.org/D74242
1 parent 4ab2ea9 commit ee9a3eb

File tree

3 files changed

+219
-2
lines changed

3 files changed

+219
-2
lines changed

llvm/lib/CodeGen/CodeGenPrepare.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2102,13 +2102,22 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB, bool &ModifiedDT
21022102
return false;
21032103

21042104
PHINode *PN = nullptr;
2105+
ExtractValueInst *EVI = nullptr;
21052106
BitCastInst *BCI = nullptr;
21062107
Value *V = RetI->getReturnValue();
21072108
if (V) {
21082109
BCI = dyn_cast<BitCastInst>(V);
21092110
if (BCI)
21102111
V = BCI->getOperand(0);
21112112

2113+
EVI = dyn_cast<ExtractValueInst>(V);
2114+
if (EVI) {
2115+
V = EVI->getOperand(0);
2116+
if (!std::all_of(EVI->idx_begin(), EVI->idx_end(),
2117+
[](unsigned idx) { return idx == 0; }))
2118+
return false;
2119+
}
2120+
21122121
PN = dyn_cast<PHINode>(V);
21132122
if (!PN)
21142123
return false;
@@ -2122,7 +2131,9 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB, bool &ModifiedDT
21222131
if (PN) {
21232132
BasicBlock::iterator BI = BB->begin();
21242133
// Skip over debug and the bitcast.
2125-
do { ++BI; } while (isa<DbgInfoIntrinsic>(BI) || &*BI == BCI);
2134+
do {
2135+
++BI;
2136+
} while (isa<DbgInfoIntrinsic>(BI) || &*BI == BCI || &*BI == EVI);
21262137
if (&*BI != RetI)
21272138
return false;
21282139
} else {

llvm/lib/Transforms/Utils/BasicBlockUtils.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,25 @@ ReturnInst *llvm::FoldReturnIntoUncondBranch(ReturnInst *RI, BasicBlock *BB,
902902
Pred->getInstList().insert(NewRet->getIterator(), NewBC);
903903
*i = NewBC;
904904
}
905+
906+
Instruction *NewEV = nullptr;
907+
if (ExtractValueInst *EVI = dyn_cast<ExtractValueInst>(V)) {
908+
V = EVI->getOperand(0);
909+
NewEV = EVI->clone();
910+
if (NewBC) {
911+
NewBC->setOperand(0, NewEV);
912+
Pred->getInstList().insert(NewBC->getIterator(), NewEV);
913+
} else {
914+
Pred->getInstList().insert(NewRet->getIterator(), NewEV);
915+
*i = NewEV;
916+
}
917+
}
918+
905919
if (PHINode *PN = dyn_cast<PHINode>(V)) {
906920
if (PN->getParent() == BB) {
907-
if (NewBC)
921+
if (NewEV) {
922+
NewEV->setOperand(0, PN->getIncomingValueForBlock(Pred));
923+
} else if (NewBC)
908924
NewBC->setOperand(0, PN->getIncomingValueForBlock(Pred));
909925
else
910926
*i = PN->getIncomingValueForBlock(Pred);
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
; RUN: llc -mtriple=x86_64-linux < %s | FileCheck %s
2+
; RUN: opt -codegenprepare -S -mtriple=x86_64-linux < %s | FileCheck %s --check-prefix OPT
3+
4+
5+
; The exit block containing extractvalue can be duplicated into the BB
6+
; containing call. And later tail call can be generated.
7+
8+
; CHECK-LABEL: test1:
9+
; CHECK: jmp bar # TAILCALL
10+
; CHECK: jmp foo # TAILCALL
11+
12+
; OPT-LABEL: test1
13+
; OPT: if.then.i:
14+
; OPT-NEXT: tail call { i8*, i64 } @bar
15+
; OPT-NEXT: extractvalue
16+
; OPT-NEXT: bitcast
17+
; OPT-NEXT: ret
18+
;
19+
; OPT: if.end.i:
20+
; OPT-NEXT: tail call { i8*, i64 } @foo
21+
; OPT-NEXT: extractvalue
22+
; OPT-NEXT: bitcast
23+
; OPT-NEXT: ret
24+
25+
define i64* @test1(i64 %size) {
26+
entry:
27+
%cmp.i.i = icmp ugt i64 %size, 16384
28+
%add.i.i = add i64 %size, 7
29+
%div.i.i = lshr i64 %add.i.i, 3
30+
%phitmp.i.i = trunc i64 %div.i.i to i32
31+
%cmp1.i = icmp eq i32 %phitmp.i.i, 0
32+
%cmp.i = or i1 %cmp.i.i, %cmp1.i
33+
br i1 %cmp.i, label %if.end.i, label %if.then.i
34+
if.then.i: ; preds = %entry
35+
%call1.i = tail call { i8*, i64 } @bar(i64 %size)
36+
br label %exit
37+
38+
if.end.i: ; preds = %entry
39+
%call2.i = tail call { i8*, i64 } @foo(i64 %size)
40+
br label %exit
41+
42+
exit:
43+
%call1.i.sink = phi { i8*, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
44+
%ev = extractvalue { i8*, i64 } %call1.i.sink, 0
45+
%result = bitcast i8* %ev to i64*
46+
ret i64* %result
47+
}
48+
49+
50+
; The extractvalue extracts a field with non-zero offset, so the exit block
51+
; can't be duplicated.
52+
53+
; CHECK-LABEL: test2:
54+
; CHECK: callq bar
55+
; CHECK: callq foo
56+
57+
; OPT-LABEL: test2
58+
; OPT: if.then.i:
59+
; OPT-NEXT: tail call { i8*, i64 } @bar
60+
; OPT-NEXT: br label %exit
61+
;
62+
; OPT: if.end.i:
63+
; OPT-NEXT: tail call { i8*, i64 } @foo
64+
; OPT-NEXT: br label %exit
65+
;
66+
; OPT: exit:
67+
; OPT-NEXT: phi
68+
; OPT-NEXT: extractvalue
69+
; OPT-NEXT: ret
70+
71+
define i64 @test2(i64 %size) {
72+
entry:
73+
%cmp.i.i = icmp ugt i64 %size, 16384
74+
%add.i.i = add i64 %size, 7
75+
%div.i.i = lshr i64 %add.i.i, 3
76+
%phitmp.i.i = trunc i64 %div.i.i to i32
77+
%cmp1.i = icmp eq i32 %phitmp.i.i, 0
78+
%cmp.i = or i1 %cmp.i.i, %cmp1.i
79+
br i1 %cmp.i, label %if.end.i, label %if.then.i
80+
if.then.i: ; preds = %entry
81+
%call1.i = tail call { i8*, i64 } @bar(i64 %size)
82+
br label %exit
83+
84+
if.end.i: ; preds = %entry
85+
%call2.i = tail call { i8*, i64 } @foo(i64 %size)
86+
br label %exit
87+
88+
exit:
89+
%call1.i.sink = phi { i8*, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
90+
%ev = extractvalue { i8*, i64 } %call1.i.sink, 1
91+
ret i64 %ev
92+
}
93+
94+
95+
; The extractvalue accesses a nest struct type, the extracted field has zero
96+
; offset, so the exit block can still be duplicated, and tail call generated.
97+
98+
; CHECK-LABEL: test3:
99+
; CHECK: jmp baz # TAILCALL
100+
; CHECK: jmp qux # TAILCALL
101+
102+
; OPT-LABEL: test3
103+
; OPT: if.then.i:
104+
; OPT-NEXT: tail call { { i8*, i64 }, i64 } @baz
105+
; OPT-NEXT: extractvalue
106+
; OPT-NEXT: bitcast
107+
; OPT-NEXT: ret
108+
;
109+
; OPT: if.end.i:
110+
; OPT-NEXT: tail call { { i8*, i64 }, i64 } @qux
111+
; OPT-NEXT: extractvalue
112+
; OPT-NEXT: bitcast
113+
; OPT-NEXT: ret
114+
115+
define i64* @test3(i64 %size) {
116+
entry:
117+
%cmp.i.i = icmp ugt i64 %size, 16384
118+
%add.i.i = add i64 %size, 7
119+
%div.i.i = lshr i64 %add.i.i, 3
120+
%phitmp.i.i = trunc i64 %div.i.i to i32
121+
%cmp1.i = icmp eq i32 %phitmp.i.i, 0
122+
%cmp.i = or i1 %cmp.i.i, %cmp1.i
123+
br i1 %cmp.i, label %if.end.i, label %if.then.i
124+
125+
if.then.i: ; preds = %entry
126+
%call1.i = tail call { {i8*, i64}, i64 } @baz(i64 %size)
127+
br label %exit
128+
129+
if.end.i: ; preds = %entry
130+
%call2.i = tail call { {i8*, i64}, i64 } @qux(i64 %size)
131+
br label %exit
132+
133+
exit:
134+
%call1.i.sink = phi { {i8*, i64}, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
135+
%ev = extractvalue { {i8*, i64}, i64 } %call1.i.sink, 0, 0
136+
%result = bitcast i8* %ev to i64*
137+
ret i64* %result
138+
}
139+
140+
141+
; The extractvalue accesses a nest struct with non-zero offset, so the exit
142+
; block can't be duplicated.
143+
144+
; CHECK-LABEL: test4:
145+
; CHECK: callq baz
146+
; CHECK: callq qux
147+
148+
; OPT-LABEL: test4
149+
; OPT: if.then.i:
150+
; OPT-NEXT: tail call { { i8*, i64 }, i64 } @baz
151+
; OPT-NEXT: br label %exit
152+
;
153+
; OPT: if.end.i:
154+
; OPT-NEXT: tail call { { i8*, i64 }, i64 } @qux
155+
; OPT-NEXT: br label %exit
156+
;
157+
; OPT: exit:
158+
; OPT-NEXT: phi
159+
; OPT-NEXT: extractvalue
160+
; OPT-NEXT: ret
161+
162+
define i64 @test4(i64 %size) {
163+
entry:
164+
%cmp.i.i = icmp ugt i64 %size, 16384
165+
%add.i.i = add i64 %size, 7
166+
%div.i.i = lshr i64 %add.i.i, 3
167+
%phitmp.i.i = trunc i64 %div.i.i to i32
168+
%cmp1.i = icmp eq i32 %phitmp.i.i, 0
169+
%cmp.i = or i1 %cmp.i.i, %cmp1.i
170+
br i1 %cmp.i, label %if.end.i, label %if.then.i
171+
172+
if.then.i: ; preds = %entry
173+
%call1.i = tail call { {i8*, i64}, i64 } @baz(i64 %size)
174+
br label %exit
175+
176+
if.end.i: ; preds = %entry
177+
%call2.i = tail call { {i8*, i64}, i64 } @qux(i64 %size)
178+
br label %exit
179+
180+
exit:
181+
%call1.i.sink = phi { {i8*, i64}, i64 } [ %call1.i, %if.then.i ], [ %call2.i, %if.end.i ]
182+
%ev = extractvalue { {i8*, i64}, i64 } %call1.i.sink, 0, 1
183+
ret i64 %ev
184+
}
185+
186+
187+
declare { i8*, i64 } @foo(i64)
188+
declare { i8*, i64 } @bar(i64)
189+
declare { {i8*, i64}, i64 } @baz(i64)
190+
declare { {i8*, i64}, i64 } @qux(i64)

0 commit comments

Comments
 (0)