Skip to content

Commit 952becc

Browse files
committed
Ensure floats are returned losslessly by the Rust ABI on 32-bit x86
1 parent dcc9a8f commit 952becc

File tree

3 files changed

+414
-0
lines changed

3 files changed

+414
-0
lines changed

compiler/rustc_ty_utils/src/abi.rs

+34
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,40 @@ fn fn_abi_adjust_for_abi<'tcx>(
744744
return;
745745
}
746746

747+
// Avoid returning floats in x87 registers on x86 as loading and storing from x87
748+
// registers will quiet signalling NaNs.
749+
if cx.tcx.sess.target.arch == "x86"
750+
&& arg_idx.is_none()
751+
// Intrinsics themselves are not actual "real" functions, so theres no need to
752+
// change their ABIs.
753+
&& abi != SpecAbi::RustIntrinsic
754+
{
755+
match arg.layout.abi {
756+
// Handle similar to the way arguments with an `Abi::Aggregate` abi are handled
757+
// below, by returning arguments up to the size of a pointer (32 bits on x86)
758+
// cast to an appropriately sized integer.
759+
Abi::Scalar(s) if s.primitive() == Float(F32) => {
760+
// Same size as a pointer, return in a register.
761+
arg.cast_to(Reg::i32());
762+
return;
763+
}
764+
Abi::Scalar(s) if s.primitive() == Float(F64) => {
765+
// Larger than a pointer, return indirectly.
766+
arg.make_indirect();
767+
return;
768+
}
769+
Abi::ScalarPair(s1, s2)
770+
if matches!(s1.primitive(), Float(F32 | F64))
771+
|| matches!(s2.primitive(), Float(F32 | F64)) =>
772+
{
773+
// Larger than a pointer, return indirectly.
774+
arg.make_indirect();
775+
return;
776+
}
777+
_ => {}
778+
};
779+
}
780+
747781
match arg.layout.abi {
748782
Abi::Aggregate { .. } => {}
749783

tests/assembly/x86-return-float.rs

+323
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
//@ assembly-output: emit-asm
2+
//@ only-x86
3+
// Force frame pointers to make ASM more consistent between targets
4+
//@ compile-flags: -O -C force-frame-pointers
5+
//@ filecheck-flags: --implicit-check-not fld --implicit-check-not fst
6+
//@ revisions: unix windows
7+
//@[unix] ignore-windows
8+
//@[windows] only-windows
9+
10+
#![crate_type = "lib"]
11+
#![feature(f16, f128)]
12+
13+
// Tests that returning `f32` and `f64` with the "Rust" ABI on 32-bit x86 doesn't use the x87
14+
// floating point stack, as loading and storing `f32`s and `f64`s to and from the x87 stack quietens
15+
// signalling NaNs.
16+
17+
// Returning individual floats
18+
19+
// CHECK-LABEL: return_f32:
20+
#[no_mangle]
21+
pub fn return_f32(x: f32) -> f32 {
22+
// CHECK: movl {{.*}}(%ebp), %eax
23+
// CHECK-NOT: ax
24+
// CHECK: retl
25+
x
26+
}
27+
28+
// CHECK-LABEL: return_f64:
29+
#[no_mangle]
30+
pub fn return_f64(x: f64) -> f64 {
31+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
32+
// CHECK-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL:.*]]
33+
// CHECK-NEXT: movsd %[[VAL]], (%[[PTR]])
34+
// CHECK: retl
35+
x
36+
}
37+
38+
// Returning scalar pairs containing floats
39+
40+
// CHECK-LABEL: return_f32_f32:
41+
#[no_mangle]
42+
pub fn return_f32_f32(x: (f32, f32)) -> (f32, f32) {
43+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
44+
// CHECK-NEXT: movss [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
45+
// CHECK-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
46+
// CHECK-NEXT: movss %[[VAL1]], (%[[PTR]])
47+
// CHECK-NEXT: movss %[[VAL2]], 4(%[[PTR]])
48+
// CHECK: retl
49+
x
50+
}
51+
52+
// CHECK-LABEL: return_f64_f64:
53+
#[no_mangle]
54+
pub fn return_f64_f64(x: (f64, f64)) -> (f64, f64) {
55+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
56+
// CHECK-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
57+
// CHECK-NEXT: movsd [[#%d,OFFSET+12]](%ebp), %[[VAL2:.*]]
58+
// CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]])
59+
// CHECK-NEXT: movsd %[[VAL2]], 8(%[[PTR]])
60+
// CHECK: retl
61+
x
62+
}
63+
64+
// CHECK-LABEL: return_f32_f64:
65+
#[no_mangle]
66+
pub fn return_f32_f64(x: (f32, f64)) -> (f32, f64) {
67+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
68+
// CHECK-NEXT: movss [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
69+
// CHECK-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
70+
// CHECK-NEXT: movss %[[VAL1]], (%[[PTR]])
71+
// CHECK-NEXT: movsd %[[VAL2]], {{4|8}}(%[[PTR]])
72+
// CHECK: retl
73+
x
74+
}
75+
76+
// CHECK-LABEL: return_f64_f32:
77+
#[no_mangle]
78+
pub fn return_f64_f32(x: (f64, f32)) -> (f64, f32) {
79+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
80+
// CHECK-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
81+
// CHECK-NEXT: movss [[#%d,OFFSET+12]](%ebp), %[[VAL2:.*]]
82+
// CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]])
83+
// CHECK-NEXT: movss %[[VAL2]], 8(%[[PTR]])
84+
// CHECK: retl
85+
x
86+
}
87+
88+
// CHECK-LABEL: return_f32_other:
89+
#[no_mangle]
90+
pub fn return_f32_other(x: (f32, usize)) -> (f32, usize) {
91+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
92+
// CHECK-NEXT: movss [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
93+
// CHECK-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
94+
// CHECK-NEXT: movss %[[VAL1]], (%[[PTR]])
95+
// CHECK-NEXT: movl %[[VAL2]], 4(%[[PTR]])
96+
// CHECK: retl
97+
x
98+
}
99+
100+
// CHECK-LABEL: return_f64_other:
101+
#[no_mangle]
102+
pub fn return_f64_other(x: (f64, usize)) -> (f64, usize) {
103+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
104+
// CHECK-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
105+
// CHECK-NEXT: movl [[#%d,OFFSET+12]](%ebp), %[[VAL2:.*]]
106+
// CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]])
107+
// CHECK-NEXT: movl %[[VAL2]], 8(%[[PTR]])
108+
// CHECK: retl
109+
x
110+
}
111+
112+
// CHECK-LABEL: return_other_f32:
113+
#[no_mangle]
114+
pub fn return_other_f32(x: (usize, f32)) -> (usize, f32) {
115+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
116+
// CHECK-NEXT: movl [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
117+
// CHECK-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
118+
// CHECK-NEXT: movl %[[VAL1]], (%[[PTR]])
119+
// CHECK-NEXT: movss %[[VAL2]], 4(%[[PTR]])
120+
// CHECK: retl
121+
x
122+
}
123+
124+
// CHECK-LABEL: return_other_f64:
125+
#[no_mangle]
126+
pub fn return_other_f64(x: (usize, f64)) -> (usize, f64) {
127+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
128+
// CHECK-NEXT: movl [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
129+
// CHECK-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
130+
// CHECK-NEXT: movl %[[VAL1]], (%[[PTR]])
131+
// CHECK-NEXT: movsd %[[VAL2]], {{4|8}}(%[[PTR]])
132+
// CHECK: retl
133+
x
134+
}
135+
136+
// Calling functions returning floats
137+
138+
// CHECK-LABEL: call_f32:
139+
#[no_mangle]
140+
pub unsafe fn call_f32(x: &mut f32) {
141+
extern "Rust" {
142+
fn get_f32() -> f32;
143+
}
144+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
145+
// CHECK: calll {{()|_}}get_f32
146+
// CHECK-NEXT: movl %eax, (%[[PTR]])
147+
*x = get_f32();
148+
}
149+
150+
// CHECK-LABEL: call_f64:
151+
#[no_mangle]
152+
pub unsafe fn call_f64(x: &mut f64) {
153+
extern "Rust" {
154+
fn get_f64() -> f64;
155+
}
156+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
157+
// CHECK: calll {{()|_}}get_f64
158+
// CHECK: movsd {{.*}}(%{{ebp|esp}}), %[[VAL:.*]]
159+
// CHECK-NEXT: movsd %[[VAL:.*]], (%[[PTR]])
160+
*x = get_f64();
161+
}
162+
163+
// Calling functions returning scalar pairs containing floats
164+
165+
// CHECK-LABEL: call_f32_f32:
166+
#[no_mangle]
167+
pub unsafe fn call_f32_f32(x: &mut (f32, f32)) {
168+
extern "Rust" {
169+
fn get_f32_f32() -> (f32, f32);
170+
}
171+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
172+
// CHECK: calll {{()|_}}get_f32_f32
173+
// CHECK: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
174+
// CHECK-NEXT: movss [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]]
175+
// CHECK-NEXT: movss %[[VAL1]], (%[[PTR]])
176+
// CHECK-NEXT: movss %[[VAL2]], 4(%[[PTR]])
177+
*x = get_f32_f32();
178+
}
179+
180+
// CHECK-LABEL: call_f64_f64:
181+
#[no_mangle]
182+
pub unsafe fn call_f64_f64(x: &mut (f64, f64)) {
183+
extern "Rust" {
184+
fn get_f64_f64() -> (f64, f64);
185+
}
186+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
187+
// CHECK: calll {{()|_}}get_f64_f64
188+
// unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
189+
// unix-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
190+
// windows: movsd (%esp), %[[VAL1:.*]]
191+
// windows-NEXT: movsd 8(%esp), %[[VAL2:.*]]
192+
// CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]])
193+
// CHECK-NEXT: movsd %[[VAL2]], 8(%[[PTR]])
194+
*x = get_f64_f64();
195+
}
196+
197+
// CHECK-LABEL: call_f32_f64:
198+
#[no_mangle]
199+
pub unsafe fn call_f32_f64(x: &mut (f32, f64)) {
200+
extern "Rust" {
201+
fn get_f32_f64() -> (f32, f64);
202+
}
203+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
204+
// CHECK: calll {{()|_}}get_f32_f64
205+
// unix: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
206+
// unix-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]]
207+
// windows: movss (%esp), %[[VAL1:.*]]
208+
// windows-NEXT: movsd 8(%esp), %[[VAL2:.*]]
209+
// CHECK-NEXT: movss %[[VAL1]], (%[[PTR]])
210+
// unix-NEXT: movsd %[[VAL2]], 4(%[[PTR]])
211+
// windows-NEXT: movsd %[[VAL2]], 8(%[[PTR]])
212+
*x = get_f32_f64();
213+
}
214+
215+
// CHECK-LABEL: call_f64_f32:
216+
#[no_mangle]
217+
pub unsafe fn call_f64_f32(x: &mut (f64, f32)) {
218+
extern "Rust" {
219+
fn get_f64_f32() -> (f64, f32);
220+
}
221+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
222+
// CHECK: calll {{()|_}}get_f64_f32
223+
// unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
224+
// unix-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
225+
// windows: movsd (%esp), %[[VAL1:.*]]
226+
// windows-NEXT: movss 8(%esp), %[[VAL2:.*]]
227+
// CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]])
228+
// CHECK-NEXT: movss %[[VAL2]], 8(%[[PTR]])
229+
*x = get_f64_f32();
230+
}
231+
232+
// CHECK-LABEL: call_f32_other:
233+
#[no_mangle]
234+
pub unsafe fn call_f32_other(x: &mut (f32, usize)) {
235+
extern "Rust" {
236+
fn get_f32_other() -> (f32, usize);
237+
}
238+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
239+
// CHECK: calll {{()|_}}get_f32_other
240+
// CHECK: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
241+
// CHECK-NEXT: movl [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]]
242+
// CHECK-NEXT: movss %[[VAL1]], (%[[PTR]])
243+
// CHECK-NEXT: movl %[[VAL2]], 4(%[[PTR]])
244+
*x = get_f32_other();
245+
}
246+
247+
// CHECK-LABEL: call_f64_other:
248+
#[no_mangle]
249+
pub unsafe fn call_f64_other(x: &mut (f64, usize)) {
250+
extern "Rust" {
251+
fn get_f64_other() -> (f64, usize);
252+
}
253+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
254+
// CHECK: calll {{()|_}}get_f64_other
255+
// unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
256+
// unix-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
257+
// windows: movsd (%esp), %[[VAL1:.*]]
258+
// windows-NEXT: movl 8(%esp), %[[VAL2:.*]]
259+
// CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]])
260+
// CHECK-NEXT: movl %[[VAL2]], 8(%[[PTR]])
261+
*x = get_f64_other();
262+
}
263+
264+
// CHECK-LABEL: call_other_f32:
265+
#[no_mangle]
266+
pub unsafe fn call_other_f32(x: &mut (usize, f32)) {
267+
extern "Rust" {
268+
fn get_other_f32() -> (usize, f32);
269+
}
270+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
271+
// CHECK: calll {{()|_}}get_other_f32
272+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
273+
// CHECK-NEXT: movss [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]]
274+
// CHECK-NEXT: movl %[[VAL1]], (%[[PTR]])
275+
// CHECK-NEXT: movss %[[VAL2]], 4(%[[PTR]])
276+
*x = get_other_f32();
277+
}
278+
279+
// CHECK-LABEL: call_other_f64:
280+
#[no_mangle]
281+
pub unsafe fn call_other_f64(x: &mut (usize, f64)) {
282+
extern "Rust" {
283+
fn get_other_f64() -> (usize, f64);
284+
}
285+
// CHECK: movl {{.*}}(%ebp), %[[PTR:.*]]
286+
// CHECK: calll {{()|_}}get_other_f64
287+
// unix: movl [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]]
288+
// unix-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]]
289+
// windows: movl (%esp), %[[VAL1:.*]]
290+
// windows-NEXT: movsd 8(%esp), %[[VAL2:.*]]
291+
// CHECK-NEXT: movl %[[VAL1]], (%[[PTR]])
292+
// unix-NEXT: movsd %[[VAL2]], 4(%[[PTR]])
293+
// windows-NEXT: movsd %[[VAL2]], 8(%[[PTR]])
294+
*x = get_other_f64();
295+
}
296+
297+
// The "C" ABI for `f16` and `f128` on x86 has never used the x87 floating point stack. Do some
298+
// basic checks to ensure this remains the case for the "Rust" ABI.
299+
300+
// CHECK-LABEL: return_f16:
301+
#[no_mangle]
302+
pub fn return_f16(x: f16) -> f16 {
303+
// CHECK: pinsrw $0, {{.*}}(%ebp), %xmm0
304+
// CHECK-NOT: xmm0
305+
// CHECK: retl
306+
x
307+
}
308+
309+
// CHECK-LABEL: return_f128:
310+
#[no_mangle]
311+
pub fn return_f128(x: f128) -> f128 {
312+
// CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]]
313+
// CHECK-NEXT: movl [[#%d,OFFSET+16]](%ebp), %[[VAL4:.*]]
314+
// CHECK-NEXT: movl [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]]
315+
// CHECK-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]]
316+
// CHECK-NEXT: movl [[#%d,OFFSET+12]](%ebp), %[[VAL3:.*]]
317+
// CHECK-NEXT: movl %[[VAL4:.*]] 12(%[[PTR]])
318+
// CHECK-NEXT: movl %[[VAL3:.*]] 8(%[[PTR]])
319+
// CHECK-NEXT: movl %[[VAL2:.*]] 4(%[[PTR]])
320+
// CHECK-NEXT: movl %[[VAL1:.*]] (%[[PTR]])
321+
// CHECK: retl
322+
x
323+
}

0 commit comments

Comments
 (0)