@@ -5,10 +5,17 @@ use rustc_span::{sym, Symbol};
5
5
use rustc_target:: abi:: { Endian , HasDataLayout } ;
6
6
7
7
use crate :: helpers:: {
8
- bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool,
8
+ bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool, ToHost ,
9
+ ToSoft ,
9
10
} ;
10
11
use crate :: * ;
11
12
13
+ #[ derive( Copy , Clone ) ]
14
+ pub ( crate ) enum MinMax {
15
+ Min ,
16
+ Max ,
17
+ }
18
+
12
19
impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
13
20
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
14
21
/// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed.
@@ -67,13 +74,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
67
74
let op = this. read_immediate ( & this. project_index ( & op, i) ?) ?;
68
75
let dest = this. project_index ( & dest, i) ?;
69
76
let val = match which {
70
- Op :: MirOp ( mir_op) => this. wrapping_unary_op ( mir_op, & op) ?. to_scalar ( ) ,
77
+ Op :: MirOp ( mir_op) => {
78
+ // This already does NaN adjustments
79
+ this. wrapping_unary_op ( mir_op, & op) ?. to_scalar ( )
80
+ }
71
81
Op :: Abs => {
72
82
// Works for f32 and f64.
73
83
let ty:: Float ( float_ty) = op. layout . ty . kind ( ) else {
74
84
span_bug ! ( this. cur_span( ) , "{} operand is not a float" , intrinsic_name)
75
85
} ;
76
86
let op = op. to_scalar ( ) ;
87
+ // "Bitwise" operation, no NaN adjustments
77
88
match float_ty {
78
89
FloatTy :: F32 => Scalar :: from_f32 ( op. to_f32 ( ) ?. abs ( ) ) ,
79
90
FloatTy :: F64 => Scalar :: from_f64 ( op. to_f64 ( ) ?. abs ( ) ) ,
@@ -86,14 +97,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
86
97
// FIXME using host floats
87
98
match float_ty {
88
99
FloatTy :: F32 => {
89
- let f = f32:: from_bits ( op. to_scalar ( ) . to_u32 ( ) ?) ;
90
- let res = f. sqrt ( ) ;
91
- Scalar :: from_u32 ( res. to_bits ( ) )
100
+ let f = op. to_scalar ( ) . to_f32 ( ) ?;
101
+ let res = f. to_host ( ) . sqrt ( ) . to_soft ( ) ;
102
+ let res = this. adjust_nan ( res, & [ f] ) ;
103
+ Scalar :: from ( res)
92
104
}
93
105
FloatTy :: F64 => {
94
- let f = f64:: from_bits ( op. to_scalar ( ) . to_u64 ( ) ?) ;
95
- let res = f. sqrt ( ) ;
96
- Scalar :: from_u64 ( res. to_bits ( ) )
106
+ let f = op. to_scalar ( ) . to_f64 ( ) ?;
107
+ let res = f. to_host ( ) . sqrt ( ) . to_soft ( ) ;
108
+ let res = this. adjust_nan ( res, & [ f] ) ;
109
+ Scalar :: from ( res)
97
110
}
98
111
}
99
112
}
@@ -105,11 +118,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
105
118
FloatTy :: F32 => {
106
119
let f = op. to_scalar ( ) . to_f32 ( ) ?;
107
120
let res = f. round_to_integral ( rounding) . value ;
121
+ let res = this. adjust_nan ( res, & [ f] ) ;
108
122
Scalar :: from_f32 ( res)
109
123
}
110
124
FloatTy :: F64 => {
111
125
let f = op. to_scalar ( ) . to_f64 ( ) ?;
112
126
let res = f. round_to_integral ( rounding) . value ;
127
+ let res = this. adjust_nan ( res, & [ f] ) ;
113
128
Scalar :: from_f64 ( res)
114
129
}
115
130
}
@@ -157,8 +172,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
157
172
enum Op {
158
173
MirOp ( BinOp ) ,
159
174
SaturatingOp ( BinOp ) ,
160
- FMax ,
161
- FMin ,
175
+ FMinMax ( MinMax ) ,
162
176
WrappingOffset ,
163
177
}
164
178
let which = match intrinsic_name {
@@ -178,8 +192,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
178
192
"le" => Op :: MirOp ( BinOp :: Le ) ,
179
193
"gt" => Op :: MirOp ( BinOp :: Gt ) ,
180
194
"ge" => Op :: MirOp ( BinOp :: Ge ) ,
181
- "fmax" => Op :: FMax ,
182
- "fmin" => Op :: FMin ,
195
+ "fmax" => Op :: FMinMax ( MinMax :: Max ) ,
196
+ "fmin" => Op :: FMinMax ( MinMax :: Min ) ,
183
197
"saturating_add" => Op :: SaturatingOp ( BinOp :: Add ) ,
184
198
"saturating_sub" => Op :: SaturatingOp ( BinOp :: Sub ) ,
185
199
"arith_offset" => Op :: WrappingOffset ,
@@ -192,6 +206,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
192
206
let dest = this. project_index ( & dest, i) ?;
193
207
let val = match which {
194
208
Op :: MirOp ( mir_op) => {
209
+ // This does NaN adjustments.
195
210
let ( val, overflowed) = this. overflowing_binary_op ( mir_op, & left, & right) ?;
196
211
if matches ! ( mir_op, BinOp :: Shl | BinOp :: Shr ) {
197
212
// Shifts have extra UB as SIMD operations that the MIR binop does not have.
@@ -225,11 +240,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
225
240
let offset_ptr = ptr. wrapping_signed_offset ( offset_bytes, this) ;
226
241
Scalar :: from_maybe_pointer ( offset_ptr, this)
227
242
}
228
- Op :: FMax => {
229
- fmax_op ( & left, & right) ?
230
- }
231
- Op :: FMin => {
232
- fmin_op ( & left, & right) ?
243
+ Op :: FMinMax ( op) => {
244
+ this. fminmax_op ( op, & left, & right) ?
233
245
}
234
246
} ;
235
247
this. write_scalar ( val, & dest) ?;
@@ -259,18 +271,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
259
271
} ;
260
272
let val = match float_ty {
261
273
FloatTy :: F32 => {
262
- let a = f32:: from_bits ( a. to_u32 ( ) ?) ;
263
- let b = f32:: from_bits ( b. to_u32 ( ) ?) ;
264
- let c = f32:: from_bits ( c. to_u32 ( ) ?) ;
265
- let res = a. mul_add ( b, c) ;
266
- Scalar :: from_u32 ( res. to_bits ( ) )
274
+ let a = a. to_f32 ( ) ?;
275
+ let b = b. to_f32 ( ) ?;
276
+ let c = c. to_f32 ( ) ?;
277
+ let res = a. to_host ( ) . mul_add ( b. to_host ( ) , c. to_host ( ) ) . to_soft ( ) ;
278
+ let res = this. adjust_nan ( res, & [ a, b, c] ) ;
279
+ Scalar :: from ( res)
267
280
}
268
281
FloatTy :: F64 => {
269
- let a = f64:: from_bits ( a. to_u64 ( ) ?) ;
270
- let b = f64:: from_bits ( b. to_u64 ( ) ?) ;
271
- let c = f64:: from_bits ( c. to_u64 ( ) ?) ;
272
- let res = a. mul_add ( b, c) ;
273
- Scalar :: from_u64 ( res. to_bits ( ) )
282
+ let a = a. to_f64 ( ) ?;
283
+ let b = b. to_f64 ( ) ?;
284
+ let c = c. to_f64 ( ) ?;
285
+ let res = a. to_host ( ) . mul_add ( b. to_host ( ) , c. to_host ( ) ) . to_soft ( ) ;
286
+ let res = this. adjust_nan ( res, & [ a, b, c] ) ;
287
+ Scalar :: from ( res)
274
288
}
275
289
} ;
276
290
this. write_scalar ( val, & dest) ?;
@@ -295,17 +309,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
295
309
enum Op {
296
310
MirOp ( BinOp ) ,
297
311
MirOpBool ( BinOp ) ,
298
- Max ,
299
- Min ,
312
+ MinMax ( MinMax ) ,
300
313
}
301
314
let which = match intrinsic_name {
302
315
"reduce_and" => Op :: MirOp ( BinOp :: BitAnd ) ,
303
316
"reduce_or" => Op :: MirOp ( BinOp :: BitOr ) ,
304
317
"reduce_xor" => Op :: MirOp ( BinOp :: BitXor ) ,
305
318
"reduce_any" => Op :: MirOpBool ( BinOp :: BitOr ) ,
306
319
"reduce_all" => Op :: MirOpBool ( BinOp :: BitAnd ) ,
307
- "reduce_max" => Op :: Max ,
308
- "reduce_min" => Op :: Min ,
320
+ "reduce_max" => Op :: MinMax ( MinMax :: Max ) ,
321
+ "reduce_min" => Op :: MinMax ( MinMax :: Min ) ,
309
322
_ => unreachable ! ( ) ,
310
323
} ;
311
324
@@ -325,24 +338,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
325
338
let op = imm_from_bool ( simd_element_to_bool ( op) ?) ;
326
339
this. wrapping_binary_op ( mir_op, & res, & op) ?
327
340
}
328
- Op :: Max => {
329
- if matches ! ( res. layout. ty. kind( ) , ty:: Float ( _) ) {
330
- ImmTy :: from_scalar ( fmax_op ( & res, & op) ?, res. layout )
331
- } else {
332
- // Just boring integers, so NaNs to worry about
333
- if this. wrapping_binary_op ( BinOp :: Ge , & res, & op) ?. to_scalar ( ) . to_bool ( ) ? {
334
- res
335
- } else {
336
- op
337
- }
338
- }
339
- }
340
- Op :: Min => {
341
+ Op :: MinMax ( mmop) => {
341
342
if matches ! ( res. layout. ty. kind( ) , ty:: Float ( _) ) {
342
- ImmTy :: from_scalar ( fmin_op ( & res, & op) ?, res. layout )
343
+ ImmTy :: from_scalar ( this . fminmax_op ( mmop , & res, & op) ?, res. layout )
343
344
} else {
344
345
// Just boring integers, so NaNs to worry about
345
- if this. wrapping_binary_op ( BinOp :: Le , & res, & op) ?. to_scalar ( ) . to_bool ( ) ? {
346
+ let mirop = match mmop {
347
+ MinMax :: Min => BinOp :: Le ,
348
+ MinMax :: Max => BinOp :: Ge ,
349
+ } ;
350
+ if this. wrapping_binary_op ( mirop, & res, & op) ?. to_scalar ( ) . to_bool ( ) ? {
346
351
res
347
352
} else {
348
353
op
@@ -709,6 +714,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
709
714
}
710
715
Ok ( ( ) )
711
716
}
717
+
718
+ fn fminmax_op (
719
+ & self ,
720
+ op : MinMax ,
721
+ left : & ImmTy < ' tcx , Provenance > ,
722
+ right : & ImmTy < ' tcx , Provenance > ,
723
+ ) -> InterpResult < ' tcx , Scalar < Provenance > > {
724
+ let this = self . eval_context_ref ( ) ;
725
+ assert_eq ! ( left. layout. ty, right. layout. ty) ;
726
+ let ty:: Float ( float_ty) = left. layout . ty . kind ( ) else {
727
+ bug ! ( "fmax operand is not a float" )
728
+ } ;
729
+ let left = left. to_scalar ( ) ;
730
+ let right = right. to_scalar ( ) ;
731
+ Ok ( match float_ty {
732
+ FloatTy :: F32 => {
733
+ let left = left. to_f32 ( ) ?;
734
+ let right = right. to_f32 ( ) ?;
735
+ let res = match op {
736
+ MinMax :: Min => left. min ( right) ,
737
+ MinMax :: Max => left. max ( right) ,
738
+ } ;
739
+ let res = this. adjust_nan ( res, & [ left, right] ) ;
740
+ Scalar :: from_f32 ( res)
741
+ }
742
+ FloatTy :: F64 => {
743
+ let left = left. to_f64 ( ) ?;
744
+ let right = right. to_f64 ( ) ?;
745
+ let res = match op {
746
+ MinMax :: Min => left. min ( right) ,
747
+ MinMax :: Max => left. max ( right) ,
748
+ } ;
749
+ let res = this. adjust_nan ( res, & [ left, right] ) ;
750
+ Scalar :: from_f64 ( res)
751
+ }
752
+ } )
753
+ }
712
754
}
713
755
714
756
fn simd_bitmask_index ( idx : u32 , vec_len : u32 , endianness : Endian ) -> u32 {
@@ -719,31 +761,3 @@ fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {
719
761
Endian :: Big => vec_len - 1 - idx, // reverse order of bits
720
762
}
721
763
}
722
-
723
- fn fmax_op < ' tcx > (
724
- left : & ImmTy < ' tcx , Provenance > ,
725
- right : & ImmTy < ' tcx , Provenance > ,
726
- ) -> InterpResult < ' tcx , Scalar < Provenance > > {
727
- assert_eq ! ( left. layout. ty, right. layout. ty) ;
728
- let ty:: Float ( float_ty) = left. layout . ty . kind ( ) else { bug ! ( "fmax operand is not a float" ) } ;
729
- let left = left. to_scalar ( ) ;
730
- let right = right. to_scalar ( ) ;
731
- Ok ( match float_ty {
732
- FloatTy :: F32 => Scalar :: from_f32 ( left. to_f32 ( ) ?. max ( right. to_f32 ( ) ?) ) ,
733
- FloatTy :: F64 => Scalar :: from_f64 ( left. to_f64 ( ) ?. max ( right. to_f64 ( ) ?) ) ,
734
- } )
735
- }
736
-
737
- fn fmin_op < ' tcx > (
738
- left : & ImmTy < ' tcx , Provenance > ,
739
- right : & ImmTy < ' tcx , Provenance > ,
740
- ) -> InterpResult < ' tcx , Scalar < Provenance > > {
741
- assert_eq ! ( left. layout. ty, right. layout. ty) ;
742
- let ty:: Float ( float_ty) = left. layout . ty . kind ( ) else { bug ! ( "fmin operand is not a float" ) } ;
743
- let left = left. to_scalar ( ) ;
744
- let right = right. to_scalar ( ) ;
745
- Ok ( match float_ty {
746
- FloatTy :: F32 => Scalar :: from_f32 ( left. to_f32 ( ) ?. min ( right. to_f32 ( ) ?) ) ,
747
- FloatTy :: F64 => Scalar :: from_f64 ( left. to_f64 ( ) ?. min ( right. to_f64 ( ) ?) ) ,
748
- } )
749
- }
0 commit comments