1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc -mtriple=riscv32 -mattr=+f -verify-machineinstrs < %s \
3; RUN:   | FileCheck -check-prefix=RV32IF %s
4; RUN: llc -mtriple=riscv64 -mattr=+f -verify-machineinstrs < %s \
5; RUN:   | FileCheck -check-prefix=RV64IF %s
6
7define dso_local float @flw(float *%a) nounwind {
8; RV32IF-LABEL: flw:
9; RV32IF:       # %bb.0:
10; RV32IF-NEXT:    flw ft0, 0(a0)
11; RV32IF-NEXT:    flw ft1, 12(a0)
12; RV32IF-NEXT:    fadd.s ft0, ft0, ft1
13; RV32IF-NEXT:    fmv.x.w a0, ft0
14; RV32IF-NEXT:    ret
15;
16; RV64IF-LABEL: flw:
17; RV64IF:       # %bb.0:
18; RV64IF-NEXT:    flw ft0, 0(a0)
19; RV64IF-NEXT:    flw ft1, 12(a0)
20; RV64IF-NEXT:    fadd.s ft0, ft0, ft1
21; RV64IF-NEXT:    fmv.x.w a0, ft0
22; RV64IF-NEXT:    ret
23  %1 = load float, float* %a
24  %2 = getelementptr float, float* %a, i32 3
25  %3 = load float, float* %2
26; Use both loaded values in an FP op to ensure an flw is used, even for the
27; soft float ABI
28  %4 = fadd float %1, %3
29  ret float %4
30}
31
32define dso_local void @fsw(float *%a, float %b, float %c) nounwind {
33; Use %b and %c in an FP op to ensure floating point registers are used, even
34; for the soft float ABI
35; RV32IF-LABEL: fsw:
36; RV32IF:       # %bb.0:
37; RV32IF-NEXT:    fmv.w.x ft0, a2
38; RV32IF-NEXT:    fmv.w.x ft1, a1
39; RV32IF-NEXT:    fadd.s ft0, ft1, ft0
40; RV32IF-NEXT:    fsw ft0, 0(a0)
41; RV32IF-NEXT:    fsw ft0, 32(a0)
42; RV32IF-NEXT:    ret
43;
44; RV64IF-LABEL: fsw:
45; RV64IF:       # %bb.0:
46; RV64IF-NEXT:    fmv.w.x ft0, a2
47; RV64IF-NEXT:    fmv.w.x ft1, a1
48; RV64IF-NEXT:    fadd.s ft0, ft1, ft0
49; RV64IF-NEXT:    fsw ft0, 0(a0)
50; RV64IF-NEXT:    fsw ft0, 32(a0)
51; RV64IF-NEXT:    ret
52  %1 = fadd float %b, %c
53  store float %1, float* %a
54  %2 = getelementptr float, float* %a, i32 8
55  store float %1, float* %2
56  ret void
57}
58
59; Check load and store to a global
60@G = dso_local global float 0.0
61
62define dso_local float @flw_fsw_global(float %a, float %b) nounwind {
63; Use %a and %b in an FP op to ensure floating point registers are used, even
64; for the soft float ABI
65; RV32IF-LABEL: flw_fsw_global:
66; RV32IF:       # %bb.0:
67; RV32IF-NEXT:    fmv.w.x ft0, a1
68; RV32IF-NEXT:    fmv.w.x ft1, a0
69; RV32IF-NEXT:    fadd.s ft0, ft1, ft0
70; RV32IF-NEXT:    lui a0, %hi(G)
71; RV32IF-NEXT:    flw ft1, %lo(G)(a0)
72; RV32IF-NEXT:    fsw ft0, %lo(G)(a0)
73; RV32IF-NEXT:    addi a1, a0, %lo(G)
74; RV32IF-NEXT:    flw ft1, 36(a1)
75; RV32IF-NEXT:    fmv.x.w a0, ft0
76; RV32IF-NEXT:    fsw ft0, 36(a1)
77; RV32IF-NEXT:    ret
78;
79; RV64IF-LABEL: flw_fsw_global:
80; RV64IF:       # %bb.0:
81; RV64IF-NEXT:    fmv.w.x ft0, a1
82; RV64IF-NEXT:    fmv.w.x ft1, a0
83; RV64IF-NEXT:    fadd.s ft0, ft1, ft0
84; RV64IF-NEXT:    lui a0, %hi(G)
85; RV64IF-NEXT:    flw ft1, %lo(G)(a0)
86; RV64IF-NEXT:    fsw ft0, %lo(G)(a0)
87; RV64IF-NEXT:    addi a1, a0, %lo(G)
88; RV64IF-NEXT:    flw ft1, 36(a1)
89; RV64IF-NEXT:    fmv.x.w a0, ft0
90; RV64IF-NEXT:    fsw ft0, 36(a1)
91; RV64IF-NEXT:    ret
92  %1 = fadd float %a, %b
93  %2 = load volatile float, float* @G
94  store float %1, float* @G
95  %3 = getelementptr float, float* @G, i32 9
96  %4 = load volatile float, float* %3
97  store float %1, float* %3
98  ret float %1
99}
100
101; Ensure that 1 is added to the high 20 bits if bit 11 of the low part is 1
102define dso_local float @flw_fsw_constant(float %a) nounwind {
103; RV32IF-LABEL: flw_fsw_constant:
104; RV32IF:       # %bb.0:
105; RV32IF-NEXT:    lui a1, 912092
106; RV32IF-NEXT:    flw ft0, -273(a1)
107; RV32IF-NEXT:    fmv.w.x ft1, a0
108; RV32IF-NEXT:    fadd.s ft0, ft1, ft0
109; RV32IF-NEXT:    fmv.x.w a0, ft0
110; RV32IF-NEXT:    fsw ft0, -273(a1)
111; RV32IF-NEXT:    ret
112;
113; RV64IF-LABEL: flw_fsw_constant:
114; RV64IF:       # %bb.0:
115; RV64IF-NEXT:    lui a1, 56
116; RV64IF-NEXT:    addiw a1, a1, -1353
117; RV64IF-NEXT:    slli a1, a1, 14
118; RV64IF-NEXT:    flw ft0, -273(a1)
119; RV64IF-NEXT:    fmv.w.x ft1, a0
120; RV64IF-NEXT:    fadd.s ft0, ft1, ft0
121; RV64IF-NEXT:    fmv.x.w a0, ft0
122; RV64IF-NEXT:    fsw ft0, -273(a1)
123; RV64IF-NEXT:    ret
124  %1 = inttoptr i32 3735928559 to float*
125  %2 = load volatile float, float* %1
126  %3 = fadd float %a, %2
127  store float %3, float* %1
128  ret float %3
129}
130
131declare void @notdead(i8*)
132
133define dso_local float @flw_stack(float %a) nounwind {
134; RV32IF-LABEL: flw_stack:
135; RV32IF:       # %bb.0:
136; RV32IF-NEXT:    addi sp, sp, -16
137; RV32IF-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
138; RV32IF-NEXT:    fmv.w.x ft0, a0
139; RV32IF-NEXT:    fsw ft0, 4(sp) # 4-byte Folded Spill
140; RV32IF-NEXT:    addi a0, sp, 8
141; RV32IF-NEXT:    call notdead@plt
142; RV32IF-NEXT:    flw ft0, 8(sp)
143; RV32IF-NEXT:    flw ft1, 4(sp) # 4-byte Folded Reload
144; RV32IF-NEXT:    fadd.s ft0, ft0, ft1
145; RV32IF-NEXT:    fmv.x.w a0, ft0
146; RV32IF-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
147; RV32IF-NEXT:    addi sp, sp, 16
148; RV32IF-NEXT:    ret
149;
150; RV64IF-LABEL: flw_stack:
151; RV64IF:       # %bb.0:
152; RV64IF-NEXT:    addi sp, sp, -16
153; RV64IF-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
154; RV64IF-NEXT:    fmv.w.x ft0, a0
155; RV64IF-NEXT:    fsw ft0, 0(sp) # 4-byte Folded Spill
156; RV64IF-NEXT:    addi a0, sp, 4
157; RV64IF-NEXT:    call notdead@plt
158; RV64IF-NEXT:    flw ft0, 4(sp)
159; RV64IF-NEXT:    flw ft1, 0(sp) # 4-byte Folded Reload
160; RV64IF-NEXT:    fadd.s ft0, ft0, ft1
161; RV64IF-NEXT:    fmv.x.w a0, ft0
162; RV64IF-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
163; RV64IF-NEXT:    addi sp, sp, 16
164; RV64IF-NEXT:    ret
165  %1 = alloca float, align 4
166  %2 = bitcast float* %1 to i8*
167  call void @notdead(i8* %2)
168  %3 = load float, float* %1
169  %4 = fadd float %3, %a ; force load in to FPR32
170  ret float %4
171}
172
173define dso_local void @fsw_stack(float %a, float %b) nounwind {
174; RV32IF-LABEL: fsw_stack:
175; RV32IF:       # %bb.0:
176; RV32IF-NEXT:    addi sp, sp, -16
177; RV32IF-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
178; RV32IF-NEXT:    fmv.w.x ft0, a1
179; RV32IF-NEXT:    fmv.w.x ft1, a0
180; RV32IF-NEXT:    fadd.s ft0, ft1, ft0
181; RV32IF-NEXT:    fsw ft0, 8(sp)
182; RV32IF-NEXT:    addi a0, sp, 8
183; RV32IF-NEXT:    call notdead@plt
184; RV32IF-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
185; RV32IF-NEXT:    addi sp, sp, 16
186; RV32IF-NEXT:    ret
187;
188; RV64IF-LABEL: fsw_stack:
189; RV64IF:       # %bb.0:
190; RV64IF-NEXT:    addi sp, sp, -16
191; RV64IF-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
192; RV64IF-NEXT:    fmv.w.x ft0, a1
193; RV64IF-NEXT:    fmv.w.x ft1, a0
194; RV64IF-NEXT:    fadd.s ft0, ft1, ft0
195; RV64IF-NEXT:    fsw ft0, 4(sp)
196; RV64IF-NEXT:    addi a0, sp, 4
197; RV64IF-NEXT:    call notdead@plt
198; RV64IF-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
199; RV64IF-NEXT:    addi sp, sp, 16
200; RV64IF-NEXT:    ret
201  %1 = fadd float %a, %b ; force store from FPR32
202  %2 = alloca float, align 4
203  store float %1, float* %2
204  %3 = bitcast float* %2 to i8*
205  call void @notdead(i8* %3)
206  ret void
207}
208