@@ -6,6 +6,7 @@ use rustc_abi::{
66 RegKind , Size , X86Call ,
77} ;
88use rustc_codegen_ssa:: MemFlags ;
9+ use rustc_codegen_ssa:: common:: RealPredicate ;
910use rustc_codegen_ssa:: mir:: operand:: { OperandRef , OperandValue } ;
1011use rustc_codegen_ssa:: mir:: place:: { PlaceRef , PlaceValue } ;
1112use rustc_codegen_ssa:: traits:: * ;
@@ -175,6 +176,9 @@ impl LlvmType for Reg {
175176
176177impl LlvmType for CastTarget {
177178 fn llvm_type < ' ll > ( & self , cx : & CodegenCx < ' ll , ' _ > ) -> & ' ll Type {
179+ if self . x87_floating_point_stack {
180+ return cx. type_x86_fp80 ( ) ;
181+ }
178182 let rest_ll_unit = self . rest . unit . llvm_type ( cx) ;
179183 let rest_count = if self . rest . total == Size :: ZERO {
180184 0
@@ -324,6 +328,7 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
324328 ) {
325329 arg_abi. store_fn_arg ( self , idx, dst)
326330 }
331+
327332 fn store_arg (
328333 & mut self ,
329334 arg_abi : & ArgAbi < ' tcx , Ty < ' tcx > > ,
@@ -332,6 +337,165 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
332337 ) {
333338 arg_abi. store ( self , val, dst)
334339 }
340+
341+ fn x87_lossless_float_to_fp_stack ( & mut self , value : & ' ll Value , no_undef : bool ) -> & ' ll Value {
342+ // If value is (partially) uninitialized (e.g. when returning `MaybeUninit<f64>`) then e.g.
343+ // branching on it could lead to undefined behaviour. To ensure that doesn't happen and that
344+ // any initialized bytes within a partially uninitialized value survive the round trip,
345+ // freeze the value.
346+ let value = if no_undef { value } else { self . freeze ( value) } ;
347+ // While we only need to manually convert sNaNs, all NaNs can be converted the same way and
348+ // checking whether `value` is NaN only takes a single floating-point x86 instruction,
349+ // whereas checking if it is a signalling NaNs requires bit operations. LLVM also generally
350+ // won't know a non-constant `value` is not a sNaN but could be a qNaN, so being more
351+ // general here doesn't prevent the branch from being optimised out in likely scenarios.
352+ let is_nan = self . fcmp ( RealPredicate :: RealUNO , value, value) ;
353+ let is_nan_block = self . append_sibling_block ( "float_pre_ret.is_nan" ) ;
354+ let is_not_nan_block = self . append_sibling_block ( "float_pre_ret.is_not_nan" ) ;
355+ let after_block = self . append_sibling_block ( "float_pre_ret.after" ) ;
356+ let dbg_loc = self . get_dbg_loc ( ) ;
357+ self . cond_br ( is_nan, is_nan_block, is_not_nan_block) ;
358+
359+ self . switch_to_block ( is_nan_block) ;
360+ if let Some ( dbg_loc) = dbg_loc {
361+ self . set_dbg_loc ( dbg_loc) ;
362+ }
363+ // This manually converts a NaN to x86_fp80 to avoid setting the quiet NaN bit of
364+ // signalling NaNs.
365+ let num_bits = self . float_width ( self . val_ty ( value) ) as u64 ;
366+ assert ! (
367+ num_bits == 32 || num_bits == 64 ,
368+ "attempt to return float on x87 floating point stack with width {num_bits}"
369+ ) ;
370+ let bits_ty = self . type_ix ( num_bits) ;
371+ let bits = self . bitcast ( value, bits_ty) ;
372+ // The high 16 bits of an x86_fp80 are the exponent and sign (the sign is the highest
373+ // bit) NaNs always have all bits of the exponent set to 1, so the only bit that is
374+ // needed from `value` is the sign bit.
375+ // Shift out the lower bits.
376+ let exp_and_sign = self . lshr ( bits, self . const_uint ( bits_ty, num_bits - 16 ) ) ;
377+ // Set the exponent to all 1s.
378+ let exp_and_sign = self . or ( exp_and_sign, self . const_uint ( bits_ty, 0x7FFF ) ) ;
379+ // Shift the exponent and sign into position.
380+ let exp_and_sign = self . zext ( exp_and_sign, self . type_ix ( 80 ) ) ;
381+ let exp_and_sign = self . shl ( exp_and_sign, self . const_uint_big ( self . type_ix ( 80 ) , 64 ) ) ;
382+
383+ // The fraction of the input NaN needs to be shifted left to just before x86_fp80's
384+ // explicit integer bit. There's no need to manually set the integer bit itself, as it
385+ // will be already set to 1 due to the all 1s exponent in the input NaN.
386+ let ( fraction_bits, fraction) = match num_bits {
387+ 32 => ( f32:: MANTISSA_DIGITS - 1 , self . zext ( bits, self . type_i64 ( ) ) ) ,
388+ 64 => ( f64:: MANTISSA_DIGITS - 1 , bits) ,
389+ _ => bug ! ( ) ,
390+ } ;
391+ // Shift the fraction into position.
392+ let fraction = self . shl ( fraction, self . const_u64 ( 63 - u64:: from ( fraction_bits) ) ) ;
393+ let fraction = self . zext ( fraction, self . type_ix ( 80 ) ) ;
394+
395+ let is_nan_res = self . or ( exp_and_sign, fraction) ;
396+ let is_nan_res = self . bitcast ( is_nan_res, self . type_x86_fp80 ( ) ) ;
397+ self . br ( after_block) ;
398+
399+ self . switch_to_block ( is_not_nan_block) ;
400+ if let Some ( dbg_loc) = dbg_loc {
401+ self . set_dbg_loc ( dbg_loc) ;
402+ }
403+ let is_not_nan_res = self . fpext ( value, self . type_x86_fp80 ( ) ) ;
404+ self . br ( after_block) ;
405+
406+ self . switch_to_block ( after_block) ;
407+ if let Some ( dbg_loc) = dbg_loc {
408+ self . set_dbg_loc ( dbg_loc) ;
409+ }
410+ self . phi (
411+ self . type_x86_fp80 ( ) ,
412+ & [ is_nan_res, is_not_nan_res] ,
413+ & [ is_nan_block, is_not_nan_block] ,
414+ )
415+ }
416+
417+ fn x87_lossless_fp_stack_to_float (
418+ & mut self ,
419+ value : & ' ll Value ,
420+ float_type : & ' ll Type ,
421+ no_undef : bool ,
422+ ) -> & ' ll Value {
423+ // If value is (partially) uninitialized (e.g. when returning `MaybeUninit<f64>`) then e.g.
424+ // branching on it could lead to undefined behaviour. To ensure that doesn't happen and that
425+ // any initialized bytes within a partially uninitialized value survive the round trip,
426+ // freeze the value.
427+ let value = if no_undef { value } else { self . freeze ( value) } ;
428+ let num_bits = self . float_width ( float_type) as u64 ;
429+ let fraction_bits = u64:: from ( match num_bits {
430+ 32 => f32:: MANTISSA_DIGITS - 1 ,
431+ 64 => f64:: MANTISSA_DIGITS - 1 ,
432+ _ => bug ! ( "attempt to return float on x87 floating point stack with width {num_bits}" ) ,
433+ } ) ;
434+ let dest_bits_type = self . type_ix ( num_bits) ;
435+ // While we only need to manually convert sNaNs, all NaNs can be converted the same way and
436+ // checking whether `value` is NaN only takes a single floating-point x86 instruction,
437+ // whereas checking if it is a signalling NaNs requires bit operations. LLVM also generally
438+ // won't know a non-constant `value` is not a sNaN but could be a qNaN, so being more
439+ // general here doesn't prevent the branch from being optimised out in likely scenarios.
440+ let is_nan = self . fcmp ( RealPredicate :: RealUNO , value, value) ;
441+ let is_nan_block = self . append_sibling_block ( "float_post_ret.is_nan" ) ;
442+ let is_not_nan_block = self . append_sibling_block ( "float_post_ret.is_not_nan" ) ;
443+ let after_block = self . append_sibling_block ( "float_post_ret.after" ) ;
444+ let dbg_loc = self . get_dbg_loc ( ) ;
445+ self . cond_br ( is_nan, is_nan_block, is_not_nan_block) ;
446+
447+ self . switch_to_block ( is_nan_block) ;
448+ if let Some ( dbg_loc) = dbg_loc {
449+ self . set_dbg_loc ( dbg_loc) ;
450+ }
451+ // This block converts a NaN to `x86_fp80` manually to avoid setting the quiet NaN bit of
452+ // signalling NaNs.
453+ // We don't handle the "invalid operand" bitpatterns here (which are treated like NaNs) as
454+ // they can't be generated by any post-80387 hardware, and the return value should have been
455+ // converted from an actual `f32`/`f64`. Even on non-SSE targets current compilers don't
456+ // seem to miscompile code so badly as to allow user-supplied `x86_fp80` "invalid operands"
457+ // to be returned as `f32`/`f64`. Similarly, sNaNs are never produced by the hardware so we
458+ // don't handle the case where the only fraction bits set are truncated, as that can never
459+ // happen with sNaNs converted from `f32`s/`f64`s.
460+ let bits = self . bitcast ( value, self . type_ix ( 80 ) ) ;
461+ // Mask out the extra 1s in the exponent as `f32`/`f64` have less bits in their
462+ // exponents than `x86_fp80`.
463+ let exp_and_sign_bits = num_bits - fraction_bits;
464+ let exp_and_sign_mask = u16:: MAX << ( 16 - exp_and_sign_bits) ;
465+ let exp_and_sign_mask = u128:: from ( exp_and_sign_mask) << 64 ;
466+ let exp_and_sign = self . and ( bits, self . const_uint_big ( self . type_ix ( 80 ) , exp_and_sign_mask) ) ;
467+ // Shift the exponent and sign into position
468+ let exp_and_sign = self
469+ . lshr ( exp_and_sign, self . const_uint_big ( self . type_ix ( 80 ) , 80 - u128:: from ( num_bits) ) ) ;
470+ let exp_and_sign = self . trunc ( exp_and_sign, dest_bits_type) ;
471+
472+ // Truncate off the exponent and sign
473+ let fraction = self . trunc ( bits, self . type_i64 ( ) ) ;
474+ // Shift the fraction in to position. There's no need to mask out `x86_fp80`'s
475+ // explicit integer bit as the fraction is right next to the exponent which is all
476+ // 1s anyway.
477+ let fraction = self . lshr ( fraction, self . const_u64 ( 63 - fraction_bits) ) ;
478+ let fraction = if num_bits != 64 { self . trunc ( fraction, dest_bits_type) } else { fraction } ;
479+
480+ // Combine the parts into the resulting float
481+ let is_nan_res = self . or ( exp_and_sign, fraction) ;
482+ let is_nan_res = self . bitcast ( is_nan_res, float_type) ;
483+ self . br ( after_block) ;
484+
485+ self . switch_to_block ( is_not_nan_block) ;
486+ if let Some ( dbg_loc) = dbg_loc {
487+ self . set_dbg_loc ( dbg_loc) ;
488+ }
489+ // Use a regular floating point conversion when the value it not a NaN.
490+ let is_not_nan_res = self . fptrunc ( value, float_type) ;
491+ self . br ( after_block) ;
492+
493+ self . switch_to_block ( after_block) ;
494+ if let Some ( dbg_loc) = dbg_loc {
495+ self . set_dbg_loc ( dbg_loc) ;
496+ }
497+ self . phi ( float_type, & [ is_nan_res, is_not_nan_res] , & [ is_nan_block, is_not_nan_block] )
498+ }
335499}
336500
337501pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
0 commit comments