xref: /llvm-project/llvm/test/CodeGen/Mips/cconv/return-struct.ll (revision c65b4d64d4b09795fe237b62a4226121c5b13248)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc -mtriple=mips-linux-gnu -relocation-model=static < %s \
3; RUN:   | FileCheck --check-prefixes=O32,O32-BE %s
4; RUN: llc -mtriple=mipsel-linux-gnu -relocation-model=static < %s \
5; RUN:   | FileCheck --check-prefixes=O32,O32-LE %s
6
7; RUN-TODO: llc -mtriple=mips64-linux-gnu -relocation-model=static -target-abi o32 < %s \
8; RUN-TODO:   | FileCheck --check-prefixes=O32 %s
9; RUN-TODO: llc -mtriple=mips64el-linux-gnu -relocation-model=static -target-abi o32 < %s \
10; RUN-TODO:   | FileCheck --check-prefixes=O32 %s
11
12; RUN: llc -mtriple=mips64-linux-gnu -relocation-model=static -target-abi n32 < %s \
13; RUN:   | FileCheck --check-prefixes=N32,N32-BE %s
14; RUN: llc -mtriple=mips64el-linux-gnu -relocation-model=static -target-abi n32 < %s \
15; RUN:   | FileCheck --check-prefixes=N32,N32-LE %s
16
17; RUN: llc -mtriple=mips64-linux-gnu -relocation-model=static -target-abi n64 < %s \
18; RUN:   | FileCheck --check-prefixes=N64,N64-BE %s
19; RUN: llc -mtriple=mips64el-linux-gnu -relocation-model=static -target-abi n64 < %s \
20; RUN:   | FileCheck --check-prefixes=N64,N64-LE %s
21
22; Test struct returns for all ABI's and byte orders.
23
24@struct_byte = global {i8} zeroinitializer
25@struct_2byte = global {i8,i8} zeroinitializer
26@struct_3xi16 = global {[3 x i16]} zeroinitializer
27@struct_6xi32 = global {[6 x i32]} zeroinitializer
28@struct_128xi16 = global {[128 x i16]} zeroinitializer
29
30declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture readonly, i64, i1)
31
32define inreg {i8} @ret_struct_i8() nounwind {
33; O32-LABEL: ret_struct_i8:
34; O32:       # %bb.0: # %entry
35; O32-NEXT:    lui $1, %hi(struct_byte)
36; O32-NEXT:    jr $ra
37; O32-NEXT:    lbu $2, %lo(struct_byte)($1)
38;
39; N32-BE-LABEL: ret_struct_i8:
40; N32-BE:       # %bb.0: # %entry
41; N32-BE-NEXT:    lui $1, %hi(struct_byte)
42; N32-BE-NEXT:    lb $1, %lo(struct_byte)($1)
43; N32-BE-NEXT:    jr $ra
44; N32-BE-NEXT:    dsll $2, $1, 56
45;
46; N32-LE-LABEL: ret_struct_i8:
47; N32-LE:       # %bb.0: # %entry
48; N32-LE-NEXT:    lui $1, %hi(struct_byte)
49; N32-LE-NEXT:    jr $ra
50; N32-LE-NEXT:    lb $2, %lo(struct_byte)($1)
51;
52; N64-BE-LABEL: ret_struct_i8:
53; N64-BE:       # %bb.0: # %entry
54; N64-BE-NEXT:    lui $1, %highest(struct_byte)
55; N64-BE-NEXT:    daddiu $1, $1, %higher(struct_byte)
56; N64-BE-NEXT:    dsll $1, $1, 16
57; N64-BE-NEXT:    daddiu $1, $1, %hi(struct_byte)
58; N64-BE-NEXT:    dsll $1, $1, 16
59; N64-BE-NEXT:    lb $1, %lo(struct_byte)($1)
60; N64-BE-NEXT:    jr $ra
61; N64-BE-NEXT:    dsll $2, $1, 56
62;
63; N64-LE-LABEL: ret_struct_i8:
64; N64-LE:       # %bb.0: # %entry
65; N64-LE-NEXT:    lui $1, %highest(struct_byte)
66; N64-LE-NEXT:    daddiu $1, $1, %higher(struct_byte)
67; N64-LE-NEXT:    dsll $1, $1, 16
68; N64-LE-NEXT:    daddiu $1, $1, %hi(struct_byte)
69; N64-LE-NEXT:    dsll $1, $1, 16
70; N64-LE-NEXT:    jr $ra
71; N64-LE-NEXT:    lb $2, %lo(struct_byte)($1)
72entry:
73        %0 = load volatile {i8}, ptr @struct_byte
74        ret {i8} %0
75}
76
77; This test is based on the way clang currently lowers {i8,i8} to {i16}.
78; FIXME: It should probably work for without any lowering too but this doesn't
79;        work as expected. Each member gets mapped to a register rather than
80;        packed into a single register.
81define inreg {i16} @ret_struct_i16() nounwind {
82; O32-LABEL: ret_struct_i16:
83; O32:       # %bb.0: # %entry
84; O32-NEXT:    addiu $sp, $sp, -8
85; O32-NEXT:    lui $1, %hi(struct_2byte)
86; O32-NEXT:    lhu $1, %lo(struct_2byte)($1)
87; O32-NEXT:    sh $1, 0($sp)
88; O32-NEXT:    lhu $2, 0($sp)
89; O32-NEXT:    jr $ra
90; O32-NEXT:    addiu $sp, $sp, 8
91;
92; N32-BE-LABEL: ret_struct_i16:
93; N32-BE:       # %bb.0: # %entry
94; N32-BE-NEXT:    addiu $sp, $sp, -16
95; N32-BE-NEXT:    lui $1, %hi(struct_2byte)
96; N32-BE-NEXT:    lhu $1, %lo(struct_2byte)($1)
97; N32-BE-NEXT:    sh $1, 8($sp)
98; N32-BE-NEXT:    lh $1, 8($sp)
99; N32-BE-NEXT:    dsll $2, $1, 48
100; N32-BE-NEXT:    jr $ra
101; N32-BE-NEXT:    addiu $sp, $sp, 16
102;
103; N32-LE-LABEL: ret_struct_i16:
104; N32-LE:       # %bb.0: # %entry
105; N32-LE-NEXT:    addiu $sp, $sp, -16
106; N32-LE-NEXT:    lui $1, %hi(struct_2byte)
107; N32-LE-NEXT:    lhu $1, %lo(struct_2byte)($1)
108; N32-LE-NEXT:    sh $1, 8($sp)
109; N32-LE-NEXT:    lh $2, 8($sp)
110; N32-LE-NEXT:    jr $ra
111; N32-LE-NEXT:    addiu $sp, $sp, 16
112;
113; N64-BE-LABEL: ret_struct_i16:
114; N64-BE:       # %bb.0: # %entry
115; N64-BE-NEXT:    daddiu $sp, $sp, -16
116; N64-BE-NEXT:    lui $1, %highest(struct_2byte)
117; N64-BE-NEXT:    daddiu $1, $1, %higher(struct_2byte)
118; N64-BE-NEXT:    dsll $1, $1, 16
119; N64-BE-NEXT:    daddiu $1, $1, %hi(struct_2byte)
120; N64-BE-NEXT:    dsll $1, $1, 16
121; N64-BE-NEXT:    lhu $1, %lo(struct_2byte)($1)
122; N64-BE-NEXT:    sh $1, 8($sp)
123; N64-BE-NEXT:    lh $1, 8($sp)
124; N64-BE-NEXT:    dsll $2, $1, 48
125; N64-BE-NEXT:    jr $ra
126; N64-BE-NEXT:    daddiu $sp, $sp, 16
127;
128; N64-LE-LABEL: ret_struct_i16:
129; N64-LE:       # %bb.0: # %entry
130; N64-LE-NEXT:    daddiu $sp, $sp, -16
131; N64-LE-NEXT:    lui $1, %highest(struct_2byte)
132; N64-LE-NEXT:    daddiu $1, $1, %higher(struct_2byte)
133; N64-LE-NEXT:    dsll $1, $1, 16
134; N64-LE-NEXT:    daddiu $1, $1, %hi(struct_2byte)
135; N64-LE-NEXT:    dsll $1, $1, 16
136; N64-LE-NEXT:    lhu $1, %lo(struct_2byte)($1)
137; N64-LE-NEXT:    sh $1, 8($sp)
138; N64-LE-NEXT:    lh $2, 8($sp)
139; N64-LE-NEXT:    jr $ra
140; N64-LE-NEXT:    daddiu $sp, $sp, 16
141entry:
142        %retval = alloca {i8,i8}, align 8
143        call void @llvm.memcpy.p0.p0.i64(ptr %retval, ptr @struct_2byte, i64 2, i1 false)
144        %0 = load volatile {i16}, ptr %retval
145        ret {i16} %0
146}
147
148; Ensure that structures bigger than 32-bits but smaller than 64-bits are
149; also returned in the upper bits on big endian targets. Previously, these were
150; missed by the CCPromoteToType and the shift didn't happen.
151define inreg {i48} @ret_struct_3xi16() nounwind {
152; O32-BE-LABEL: ret_struct_3xi16:
153; O32-BE:       # %bb.0: # %entry
154; O32-BE-NEXT:    lui $1, %hi(struct_3xi16)
155; O32-BE-NEXT:    lw $2, %lo(struct_3xi16)($1)
156; O32-BE-NEXT:    sll $3, $2, 16
157; O32-BE-NEXT:    addiu $1, $1, %lo(struct_3xi16)
158; O32-BE-NEXT:    lhu $1, 4($1)
159; O32-BE-NEXT:    or $3, $1, $3
160; O32-BE-NEXT:    jr $ra
161; O32-BE-NEXT:    srl $2, $2, 16
162;
163; O32-LE-LABEL: ret_struct_3xi16:
164; O32-LE:       # %bb.0: # %entry
165; O32-LE-NEXT:    lui $1, %hi(struct_3xi16)
166; O32-LE-NEXT:    lw $2, %lo(struct_3xi16)($1)
167; O32-LE-NEXT:    addiu $1, $1, %lo(struct_3xi16)
168; O32-LE-NEXT:    jr $ra
169; O32-LE-NEXT:    lhu $3, 4($1)
170;
171; N32-BE-LABEL: ret_struct_3xi16:
172; N32-BE:       # %bb.0: # %entry
173; N32-BE-NEXT:    lui $1, %hi(struct_3xi16)
174; N32-BE-NEXT:    lw $2, %lo(struct_3xi16)($1)
175; N32-BE-NEXT:    dsll $2, $2, 32
176; N32-BE-NEXT:    addiu $1, $1, %lo(struct_3xi16)
177; N32-BE-NEXT:    lhu $1, 4($1)
178; N32-BE-NEXT:    dsll $1, $1, 16
179; N32-BE-NEXT:    jr $ra
180; N32-BE-NEXT:    or $2, $2, $1
181;
182; N32-LE-LABEL: ret_struct_3xi16:
183; N32-LE:       # %bb.0: # %entry
184; N32-LE-NEXT:    lui $1, %hi(struct_3xi16)
185; N32-LE-NEXT:    lwu $2, %lo(struct_3xi16)($1)
186; N32-LE-NEXT:    addiu $1, $1, %lo(struct_3xi16)
187; N32-LE-NEXT:    lh $1, 4($1)
188; N32-LE-NEXT:    dsll $1, $1, 32
189; N32-LE-NEXT:    jr $ra
190; N32-LE-NEXT:    or $2, $2, $1
191;
192; N64-BE-LABEL: ret_struct_3xi16:
193; N64-BE:       # %bb.0: # %entry
194; N64-BE-NEXT:    lui $1, %highest(struct_3xi16)
195; N64-BE-NEXT:    daddiu $1, $1, %higher(struct_3xi16)
196; N64-BE-NEXT:    dsll $1, $1, 16
197; N64-BE-NEXT:    daddiu $1, $1, %hi(struct_3xi16)
198; N64-BE-NEXT:    dsll $1, $1, 16
199; N64-BE-NEXT:    lw $2, %lo(struct_3xi16)($1)
200; N64-BE-NEXT:    dsll $2, $2, 32
201; N64-BE-NEXT:    daddiu $1, $1, %lo(struct_3xi16)
202; N64-BE-NEXT:    lhu $1, 4($1)
203; N64-BE-NEXT:    dsll $1, $1, 16
204; N64-BE-NEXT:    jr $ra
205; N64-BE-NEXT:    or $2, $2, $1
206;
207; N64-LE-LABEL: ret_struct_3xi16:
208; N64-LE:       # %bb.0: # %entry
209; N64-LE-NEXT:    lui $1, %highest(struct_3xi16)
210; N64-LE-NEXT:    daddiu $1, $1, %higher(struct_3xi16)
211; N64-LE-NEXT:    dsll $1, $1, 16
212; N64-LE-NEXT:    daddiu $1, $1, %hi(struct_3xi16)
213; N64-LE-NEXT:    dsll $1, $1, 16
214; N64-LE-NEXT:    lwu $2, %lo(struct_3xi16)($1)
215; N64-LE-NEXT:    daddiu $1, $1, %lo(struct_3xi16)
216; N64-LE-NEXT:    lh $1, 4($1)
217; N64-LE-NEXT:    dsll $1, $1, 32
218; N64-LE-NEXT:    jr $ra
219; N64-LE-NEXT:    or $2, $2, $1
220entry:
221        %0 = load volatile i48, ptr @struct_3xi16, align 2
222        %1 = insertvalue {i48} undef, i48 %0, 0
223        ret {i48} %1
224}
225
226; Ensure that large structures (>128-bit) are returned indirectly.
227; We pick an extremely large structure so we don't have to match inlined memcpy's.
228define void @ret_struct_128xi16(ptr sret({[128 x i16]}) %returnval) {
229; O32-LABEL: ret_struct_128xi16:
230; O32:       # %bb.0: # %entry
231; O32-NEXT:    addiu $sp, $sp, -24
232; O32-NEXT:    .cfi_def_cfa_offset 24
233; O32-NEXT:    sw $ra, 20($sp) # 4-byte Folded Spill
234; O32-NEXT:    sw $16, 16($sp) # 4-byte Folded Spill
235; O32-NEXT:    .cfi_offset 31, -4
236; O32-NEXT:    .cfi_offset 16, -8
237; O32-NEXT:    move $16, $4
238; O32-NEXT:    lui $1, %hi(struct_128xi16)
239; O32-NEXT:    addiu $5, $1, %lo(struct_128xi16)
240; O32-NEXT:    jal memcpy
241; O32-NEXT:    addiu $6, $zero, 256
242; O32-NEXT:    move $2, $16
243; O32-NEXT:    lw $16, 16($sp) # 4-byte Folded Reload
244; O32-NEXT:    lw $ra, 20($sp) # 4-byte Folded Reload
245; O32-NEXT:    jr $ra
246; O32-NEXT:    addiu $sp, $sp, 24
247;
248; N32-LABEL: ret_struct_128xi16:
249; N32:       # %bb.0: # %entry
250; N32-NEXT:    addiu $sp, $sp, -16
251; N32-NEXT:    .cfi_def_cfa_offset 16
252; N32-NEXT:    sd $ra, 8($sp) # 8-byte Folded Spill
253; N32-NEXT:    sd $16, 0($sp) # 8-byte Folded Spill
254; N32-NEXT:    .cfi_offset 31, -8
255; N32-NEXT:    .cfi_offset 16, -16
256; N32-NEXT:    lui $1, %hi(struct_128xi16)
257; N32-NEXT:    addiu $5, $1, %lo(struct_128xi16)
258; N32-NEXT:    sll $16, $4, 0
259; N32-NEXT:    jal memcpy
260; N32-NEXT:    daddiu $6, $zero, 256
261; N32-NEXT:    move $2, $16
262; N32-NEXT:    ld $16, 0($sp) # 8-byte Folded Reload
263; N32-NEXT:    ld $ra, 8($sp) # 8-byte Folded Reload
264; N32-NEXT:    jr $ra
265; N32-NEXT:    addiu $sp, $sp, 16
266;
267; N64-LABEL: ret_struct_128xi16:
268; N64:       # %bb.0: # %entry
269; N64-NEXT:    daddiu $sp, $sp, -16
270; N64-NEXT:    .cfi_def_cfa_offset 16
271; N64-NEXT:    sd $ra, 8($sp) # 8-byte Folded Spill
272; N64-NEXT:    sd $16, 0($sp) # 8-byte Folded Spill
273; N64-NEXT:    .cfi_offset 31, -8
274; N64-NEXT:    .cfi_offset 16, -16
275; N64-NEXT:    move $16, $4
276; N64-NEXT:    lui $1, %highest(struct_128xi16)
277; N64-NEXT:    daddiu $1, $1, %higher(struct_128xi16)
278; N64-NEXT:    dsll $1, $1, 16
279; N64-NEXT:    daddiu $1, $1, %hi(struct_128xi16)
280; N64-NEXT:    dsll $1, $1, 16
281; N64-NEXT:    daddiu $5, $1, %lo(struct_128xi16)
282; N64-NEXT:    jal memcpy
283; N64-NEXT:    daddiu $6, $zero, 256
284; N64-NEXT:    move $2, $16
285; N64-NEXT:    ld $16, 0($sp) # 8-byte Folded Reload
286; N64-NEXT:    ld $ra, 8($sp) # 8-byte Folded Reload
287; N64-NEXT:    jr $ra
288; N64-NEXT:    daddiu $sp, $sp, 16
289entry:
290        call void @llvm.memcpy.p0.p0.i64(ptr align 2 %returnval, ptr align 2 @struct_128xi16, i64 256, i1 false)
291        ret void
292}
293
294; Ensure that large structures (>128-bit) are returned indirectly.
295; This will generate inlined memcpy's anyway so pick the smallest large
296; structure
297; This time we let the backend lower the sret argument.
298define {[6 x i32]} @ret_struct_6xi32() {
299; O32-LABEL: ret_struct_6xi32:
300; O32:       # %bb.0: # %entry
301; O32-NEXT:    lui $1, %hi(struct_6xi32)
302; O32-NEXT:    lw $2, %lo(struct_6xi32)($1)
303; O32-NEXT:    addiu $1, $1, %lo(struct_6xi32)
304; O32-NEXT:    lw $3, 4($1)
305; O32-NEXT:    lw $5, 8($1)
306; O32-NEXT:    lw $6, 12($1)
307; O32-NEXT:    lw $7, 16($1)
308; O32-NEXT:    lw $1, 20($1)
309; O32-NEXT:    sw $1, 20($4)
310; O32-NEXT:    sw $7, 16($4)
311; O32-NEXT:    sw $6, 12($4)
312; O32-NEXT:    sw $5, 8($4)
313; O32-NEXT:    sw $3, 4($4)
314; O32-NEXT:    jr $ra
315; O32-NEXT:    sw $2, 0($4)
316;
317; N32-LABEL: ret_struct_6xi32:
318; N32:       # %bb.0: # %entry
319; N32-NEXT:    sll $1, $4, 0
320; N32-NEXT:    lui $2, %hi(struct_6xi32)
321; N32-NEXT:    lw $3, %lo(struct_6xi32)($2)
322; N32-NEXT:    addiu $2, $2, %lo(struct_6xi32)
323; N32-NEXT:    lw $4, 4($2)
324; N32-NEXT:    lw $5, 8($2)
325; N32-NEXT:    lw $6, 12($2)
326; N32-NEXT:    lw $7, 16($2)
327; N32-NEXT:    lw $2, 20($2)
328; N32-NEXT:    sw $2, 20($1)
329; N32-NEXT:    sw $7, 16($1)
330; N32-NEXT:    sw $6, 12($1)
331; N32-NEXT:    sw $5, 8($1)
332; N32-NEXT:    sw $4, 4($1)
333; N32-NEXT:    jr $ra
334; N32-NEXT:    sw $3, 0($1)
335;
336; N64-LABEL: ret_struct_6xi32:
337; N64:       # %bb.0: # %entry
338; N64-NEXT:    lui $1, %highest(struct_6xi32)
339; N64-NEXT:    daddiu $1, $1, %higher(struct_6xi32)
340; N64-NEXT:    dsll $1, $1, 16
341; N64-NEXT:    daddiu $1, $1, %hi(struct_6xi32)
342; N64-NEXT:    dsll $1, $1, 16
343; N64-NEXT:    lw $2, %lo(struct_6xi32)($1)
344; N64-NEXT:    daddiu $1, $1, %lo(struct_6xi32)
345; N64-NEXT:    lw $3, 4($1)
346; N64-NEXT:    lw $5, 8($1)
347; N64-NEXT:    lw $6, 12($1)
348; N64-NEXT:    lw $7, 16($1)
349; N64-NEXT:    lw $1, 20($1)
350; N64-NEXT:    sw $1, 20($4)
351; N64-NEXT:    sw $7, 16($4)
352; N64-NEXT:    sw $6, 12($4)
353; N64-NEXT:    sw $5, 8($4)
354; N64-NEXT:    sw $3, 4($4)
355; N64-NEXT:    jr $ra
356; N64-NEXT:    sw $2, 0($4)
357entry:
358        %0 = load volatile {[6 x i32]}, ptr @struct_6xi32, align 2
359        ret {[6 x i32]} %0
360}
361