1; RUN: llc < %s -mtriple=armv6-linux-gnueabi -arm-tail-calls | FileCheck %s -check-prefix=CHECKELF
2; RUN: llc < %s -mtriple=thumbv7-apple-ios -arm-tail-calls | FileCheck %s -check-prefix=CHECKT2D
3
4declare i16 @identity16(i16 returned %x)
5declare i32 @identity32(i32 returned %x)
6declare zeroext i16 @retzext16(i16 returned %x)
7declare i16 @paramzext16(i16 zeroext returned %x)
8declare zeroext i16 @bothzext16(i16 zeroext returned %x)
9
10; The zeroext param attribute below is meant to have no effect
11define i16 @test_identity(i16 zeroext %x) {
12entry:
13; CHECKELF-LABEL: test_identity:
14; CHECKELF: mov [[SAVEX:r[0-9]+]], r0
15; CHECKELF: bl identity16
16; CHECKELF: uxth r0, r0
17; CHECKELF: bl identity32
18; CHECKELF: mov r0, [[SAVEX]]
19; CHECKT2D-LABEL: test_identity:
20; CHECKT2D: mov [[SAVEX:r[0-9]+]], r0
21; CHECKT2D: blx _identity16
22; CHECKT2D: uxth r0, r0
23; CHECKT2D: blx _identity32
24; CHECKT2D: mov r0, [[SAVEX]]
25  %call = tail call i16 @identity16(i16 %x)
26  %b = zext i16 %call to i32
27  %call2 = tail call i32 @identity32(i32 %b)
28  ret i16 %x
29}
30
31; FIXME: This ought not to require register saving but currently does because
32; x is not considered equal to %call (see SelectionDAGBuilder.cpp)
33define i16 @test_matched_ret(i16 %x) {
34entry:
35; CHECKELF-LABEL: test_matched_ret:
36
37; This shouldn't be required
38; CHECKELF: mov [[SAVEX:r[0-9]+]], r0
39
40; CHECKELF: bl retzext16
41; CHECKELF-NOT: uxth r0, {{r[0-9]+}}
42; CHECKELF: bl identity32
43
44; This shouldn't be required
45; CHECKELF: mov r0, [[SAVEX]]
46
47; CHECKT2D-LABEL: test_matched_ret:
48
49; This shouldn't be required
50; CHECKT2D: mov [[SAVEX:r[0-9]+]], r0
51
52; CHECKT2D: blx _retzext16
53; CHECKT2D-NOT: uxth r0, {{r[0-9]+}}
54; CHECKT2D: blx _identity32
55
56; This shouldn't be required
57; CHECKT2D: mov r0, [[SAVEX]]
58
59  %call = tail call i16 @retzext16(i16 %x)
60  %b = zext i16 %call to i32
61  %call2 = tail call i32 @identity32(i32 %b)
62  ret i16 %x
63}
64
65define i16 @test_mismatched_ret(i16 %x) {
66entry:
67; CHECKELF-LABEL: test_mismatched_ret:
68; CHECKELF: mov [[SAVEX:r[0-9]+]], r0
69; CHECKELF: bl retzext16
70; CHECKELF: sxth r0, {{r[0-9]+}}
71; CHECKELF: bl identity32
72; CHECKELF: mov r0, [[SAVEX]]
73; CHECKT2D-LABEL: test_mismatched_ret:
74; CHECKT2D: mov [[SAVEX:r[0-9]+]], r0
75; CHECKT2D: blx _retzext16
76; CHECKT2D: sxth r0, {{r[0-9]+}}
77; CHECKT2D: blx _identity32
78; CHECKT2D: mov r0, [[SAVEX]]
79  %call = tail call i16 @retzext16(i16 %x)
80  %b = sext i16 %call to i32
81  %call2 = tail call i32 @identity32(i32 %b)
82  ret i16 %x
83}
84
85define i16 @test_matched_paramext(i16 %x) {
86entry:
87; CHECKELF-LABEL: test_matched_paramext:
88; CHECKELF: uxth r0, r0
89; CHECKELF: bl paramzext16
90; CHECKELF: uxth r0, r0
91; CHECKELF: bl identity32
92; CHECKELF: b paramzext16
93; CHECKT2D-LABEL: test_matched_paramext:
94; CHECKT2D: uxth r0, r0
95; CHECKT2D: blx _paramzext16
96; CHECKT2D: uxth r0, r0
97; CHECKT2D: blx _identity32
98; CHECKT2D: b.w _paramzext16
99  %call = tail call i16 @paramzext16(i16 %x)
100  %b = zext i16 %call to i32
101  %call2 = tail call i32 @identity32(i32 %b)
102  %call3 = tail call i16 @paramzext16(i16 %call)
103  ret i16 %call3
104}
105
106; FIXME: This theoretically ought to optimize to exact same output as the
107; version above, but doesn't currently (see SelectionDAGBuilder.cpp)
108define i16 @test_matched_paramext2(i16 %x) {
109entry:
110
111; Since there doesn't seem to be an unambiguous optimal selection and
112; scheduling of uxth and mov instructions below in lieu of the 'returned'
113; optimization, don't bother checking: just verify that the calls are made
114; in the correct order as a basic sanity check
115
116; CHECKELF-LABEL: test_matched_paramext2:
117; CHECKELF: bl paramzext16
118; CHECKELF: bl identity32
119; CHECKELF: b paramzext16
120; CHECKT2D-LABEL: test_matched_paramext2:
121; CHECKT2D: blx _paramzext16
122; CHECKT2D: blx _identity32
123; CHECKT2D: b.w _paramzext16
124  %call = tail call i16 @paramzext16(i16 %x)
125
126; Should make no difference if %x is used below rather than %call, but it does
127  %b = zext i16 %x to i32
128
129  %call2 = tail call i32 @identity32(i32 %b)
130  %call3 = tail call i16 @paramzext16(i16 %call)
131  ret i16 %call3
132}
133
134define i16 @test_matched_bothext(i16 %x) {
135entry:
136; CHECKELF-LABEL: test_matched_bothext:
137; CHECKELF: uxth r0, r0
138; CHECKELF: bl bothzext16
139; CHECKELF-NOT: uxth r0, r0
140
141; FIXME: Tail call should be OK here
142; CHECKELF: bl identity32
143
144; CHECKT2D-LABEL: test_matched_bothext:
145; CHECKT2D: uxth r0, r0
146; CHECKT2D: blx _bothzext16
147; CHECKT2D-NOT: uxth r0, r0
148
149; FIXME: Tail call should be OK here
150; CHECKT2D: blx _identity32
151
152  %call = tail call i16 @bothzext16(i16 %x)
153  %b = zext i16 %x to i32
154  %call2 = tail call i32 @identity32(i32 %b)
155  ret i16 %call
156}
157
158define i16 @test_mismatched_bothext(i16 %x) {
159entry:
160; CHECKELF-LABEL: test_mismatched_bothext:
161; CHECKELF: mov [[SAVEX:r[0-9]+]], r0
162; CHECKELF: uxth r0, {{r[0-9]+}}
163; CHECKELF: bl bothzext16
164; CHECKELF: sxth r0, [[SAVEX]]
165; CHECKELF: bl identity32
166; CHECKELF: mov r0, [[SAVEX]]
167; CHECKT2D-LABEL: test_mismatched_bothext:
168; CHECKT2D: mov [[SAVEX:r[0-9]+]], r0
169; CHECKT2D: uxth r0, {{r[0-9]+}}
170; CHECKT2D: blx _bothzext16
171; CHECKT2D: sxth r0, [[SAVEX]]
172; CHECKT2D: blx _identity32
173; CHECKT2D: mov r0, [[SAVEX]]
174  %call = tail call i16 @bothzext16(i16 %x)
175  %b = sext i16 %x to i32
176  %call2 = tail call i32 @identity32(i32 %b)
177  ret i16 %x
178}
179