]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/num.rs
Auto merge of #107768 - matthiaskrgr:rollup-9u4cal4, r=matthiaskrgr
[rust.git] / compiler / rustc_codegen_cranelift / src / num.rs
1 //! Various operations on integer and floating-point numbers
2
3 use crate::prelude::*;
4
5 pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option<IntCC> {
6     use BinOp::*;
7     use IntCC::*;
8     Some(match bin_op {
9         Eq => Equal,
10         Lt => {
11             if signed {
12                 SignedLessThan
13             } else {
14                 UnsignedLessThan
15             }
16         }
17         Le => {
18             if signed {
19                 SignedLessThanOrEqual
20             } else {
21                 UnsignedLessThanOrEqual
22             }
23         }
24         Ne => NotEqual,
25         Ge => {
26             if signed {
27                 SignedGreaterThanOrEqual
28             } else {
29                 UnsignedGreaterThanOrEqual
30             }
31         }
32         Gt => {
33             if signed {
34                 SignedGreaterThan
35             } else {
36                 UnsignedGreaterThan
37             }
38         }
39         _ => return None,
40     })
41 }
42
43 fn codegen_compare_bin_op<'tcx>(
44     fx: &mut FunctionCx<'_, '_, 'tcx>,
45     bin_op: BinOp,
46     signed: bool,
47     lhs: Value,
48     rhs: Value,
49 ) -> CValue<'tcx> {
50     let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap();
51     let val = fx.bcx.ins().icmp(intcc, lhs, rhs);
52     CValue::by_val(val, fx.layout_of(fx.tcx.types.bool))
53 }
54
55 pub(crate) fn codegen_binop<'tcx>(
56     fx: &mut FunctionCx<'_, '_, 'tcx>,
57     bin_op: BinOp,
58     in_lhs: CValue<'tcx>,
59     in_rhs: CValue<'tcx>,
60 ) -> CValue<'tcx> {
61     match bin_op {
62         BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
63             match in_lhs.layout().ty.kind() {
64                 ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => {
65                     let signed = type_sign(in_lhs.layout().ty);
66                     let lhs = in_lhs.load_scalar(fx);
67                     let rhs = in_rhs.load_scalar(fx);
68
69                     return codegen_compare_bin_op(fx, bin_op, signed, lhs, rhs);
70                 }
71                 _ => {}
72             }
73         }
74         _ => {}
75     }
76
77     match in_lhs.layout().ty.kind() {
78         ty::Bool => crate::num::codegen_bool_binop(fx, bin_op, in_lhs, in_rhs),
79         ty::Uint(_) | ty::Int(_) => crate::num::codegen_int_binop(fx, bin_op, in_lhs, in_rhs),
80         ty::Float(_) => crate::num::codegen_float_binop(fx, bin_op, in_lhs, in_rhs),
81         ty::RawPtr(..) | ty::FnPtr(..) => crate::num::codegen_ptr_binop(fx, bin_op, in_lhs, in_rhs),
82         _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
83     }
84 }
85
86 pub(crate) fn codegen_bool_binop<'tcx>(
87     fx: &mut FunctionCx<'_, '_, 'tcx>,
88     bin_op: BinOp,
89     in_lhs: CValue<'tcx>,
90     in_rhs: CValue<'tcx>,
91 ) -> CValue<'tcx> {
92     let lhs = in_lhs.load_scalar(fx);
93     let rhs = in_rhs.load_scalar(fx);
94
95     let b = fx.bcx.ins();
96     let res = match bin_op {
97         BinOp::BitXor => b.bxor(lhs, rhs),
98         BinOp::BitAnd => b.band(lhs, rhs),
99         BinOp::BitOr => b.bor(lhs, rhs),
100         // Compare binops handles by `codegen_binop`.
101         _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
102     };
103
104     CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
105 }
106
107 pub(crate) fn codegen_int_binop<'tcx>(
108     fx: &mut FunctionCx<'_, '_, 'tcx>,
109     bin_op: BinOp,
110     in_lhs: CValue<'tcx>,
111     in_rhs: CValue<'tcx>,
112 ) -> CValue<'tcx> {
113     if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
114         assert_eq!(
115             in_lhs.layout().ty,
116             in_rhs.layout().ty,
117             "int binop requires lhs and rhs of same type"
118         );
119     }
120
121     if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, false, in_lhs, in_rhs) {
122         return res;
123     }
124
125     let signed = type_sign(in_lhs.layout().ty);
126
127     let lhs = in_lhs.load_scalar(fx);
128     let rhs = in_rhs.load_scalar(fx);
129
130     let b = fx.bcx.ins();
131     let val = match bin_op {
132         BinOp::Add => b.iadd(lhs, rhs),
133         BinOp::Sub => b.isub(lhs, rhs),
134         BinOp::Mul => b.imul(lhs, rhs),
135         BinOp::Div => {
136             if signed {
137                 b.sdiv(lhs, rhs)
138             } else {
139                 b.udiv(lhs, rhs)
140             }
141         }
142         BinOp::Rem => {
143             if signed {
144                 b.srem(lhs, rhs)
145             } else {
146                 b.urem(lhs, rhs)
147             }
148         }
149         BinOp::BitXor => b.bxor(lhs, rhs),
150         BinOp::BitAnd => b.band(lhs, rhs),
151         BinOp::BitOr => b.bor(lhs, rhs),
152         BinOp::Shl => b.ishl(lhs, rhs),
153         BinOp::Shr => {
154             if signed {
155                 b.sshr(lhs, rhs)
156             } else {
157                 b.ushr(lhs, rhs)
158             }
159         }
160         // Compare binops handles by `codegen_binop`.
161         _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
162     };
163
164     CValue::by_val(val, in_lhs.layout())
165 }
166
167 pub(crate) fn codegen_checked_int_binop<'tcx>(
168     fx: &mut FunctionCx<'_, '_, 'tcx>,
169     bin_op: BinOp,
170     in_lhs: CValue<'tcx>,
171     in_rhs: CValue<'tcx>,
172 ) -> CValue<'tcx> {
173     if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
174         assert_eq!(
175             in_lhs.layout().ty,
176             in_rhs.layout().ty,
177             "checked int binop requires lhs and rhs of same type"
178         );
179     }
180
181     let lhs = in_lhs.load_scalar(fx);
182     let rhs = in_rhs.load_scalar(fx);
183
184     if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) {
185         return res;
186     }
187
188     let signed = type_sign(in_lhs.layout().ty);
189
190     let (res, has_overflow) = match bin_op {
191         BinOp::Add => {
192             /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs);
193             (val, c_out)*/
194             // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16
195             let val = fx.bcx.ins().iadd(lhs, rhs);
196             let has_overflow = if !signed {
197                 fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs)
198             } else {
199                 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
200                 let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs);
201                 fx.bcx.ins().bxor(rhs_is_negative, slt)
202             };
203             (val, has_overflow)
204         }
205         BinOp::Sub => {
206             /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs);
207             (val, b_out)*/
208             // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16
209             let val = fx.bcx.ins().isub(lhs, rhs);
210             let has_overflow = if !signed {
211                 fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs)
212             } else {
213                 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
214                 let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs);
215                 fx.bcx.ins().bxor(rhs_is_negative, sgt)
216             };
217             (val, has_overflow)
218         }
219         BinOp::Mul => {
220             let ty = fx.bcx.func.dfg.value_type(lhs);
221             match ty {
222                 types::I8 | types::I16 | types::I32 if !signed => {
223                     let lhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), lhs);
224                     let rhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), rhs);
225                     let val = fx.bcx.ins().imul(lhs, rhs);
226                     let has_overflow = fx.bcx.ins().icmp_imm(
227                         IntCC::UnsignedGreaterThan,
228                         val,
229                         (1 << ty.bits()) - 1,
230                     );
231                     let val = fx.bcx.ins().ireduce(ty, val);
232                     (val, has_overflow)
233                 }
234                 types::I8 | types::I16 | types::I32 if signed => {
235                     let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs);
236                     let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs);
237                     let val = fx.bcx.ins().imul(lhs, rhs);
238                     let has_underflow =
239                         fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1)));
240                     let has_overflow = fx.bcx.ins().icmp_imm(
241                         IntCC::SignedGreaterThan,
242                         val,
243                         (1 << (ty.bits() - 1)) - 1,
244                     );
245                     let val = fx.bcx.ins().ireduce(ty, val);
246                     (val, fx.bcx.ins().bor(has_underflow, has_overflow))
247                 }
248                 types::I64 => {
249                     let val = fx.bcx.ins().imul(lhs, rhs);
250                     let has_overflow = if !signed {
251                         let val_hi = fx.bcx.ins().umulhi(lhs, rhs);
252                         fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0)
253                     } else {
254                         // Based on LLVM's instruction sequence for compiling
255                         // a.checked_mul(b).is_some() to riscv64gc:
256                         // mulh    a2, a0, a1
257                         // mul     a0, a0, a1
258                         // srai    a0, a0, 63
259                         // xor     a0, a0, a2
260                         // snez    a0, a0
261                         let val_hi = fx.bcx.ins().smulhi(lhs, rhs);
262                         let val_sign = fx.bcx.ins().sshr_imm(val, i64::from(ty.bits() - 1));
263                         let xor = fx.bcx.ins().bxor(val_hi, val_sign);
264                         fx.bcx.ins().icmp_imm(IntCC::NotEqual, xor, 0)
265                     };
266                     (val, has_overflow)
267                 }
268                 types::I128 => {
269                     unreachable!("i128 should have been handled by codegen_i128::maybe_codegen")
270                 }
271                 _ => unreachable!("invalid non-integer type {}", ty),
272             }
273         }
274         BinOp::Shl => {
275             let val = fx.bcx.ins().ishl(lhs, rhs);
276             let ty = fx.bcx.func.dfg.value_type(val);
277             let max_shift = i64::from(ty.bits()) - 1;
278             let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
279             (val, has_overflow)
280         }
281         BinOp::Shr => {
282             let val =
283                 if !signed { fx.bcx.ins().ushr(lhs, rhs) } else { fx.bcx.ins().sshr(lhs, rhs) };
284             let ty = fx.bcx.func.dfg.value_type(val);
285             let max_shift = i64::from(ty.bits()) - 1;
286             let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
287             (val, has_overflow)
288         }
289         _ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
290     };
291
292     let out_layout = fx.layout_of(fx.tcx.mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()));
293     CValue::by_val_pair(res, has_overflow, out_layout)
294 }
295
296 pub(crate) fn codegen_saturating_int_binop<'tcx>(
297     fx: &mut FunctionCx<'_, '_, 'tcx>,
298     bin_op: BinOp,
299     lhs: CValue<'tcx>,
300     rhs: CValue<'tcx>,
301 ) -> CValue<'tcx> {
302     assert_eq!(lhs.layout().ty, rhs.layout().ty);
303
304     let signed = type_sign(lhs.layout().ty);
305     let clif_ty = fx.clif_type(lhs.layout().ty).unwrap();
306     let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed);
307
308     let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs);
309     let (val, has_overflow) = checked_res.load_scalar_pair(fx);
310
311     let val = match (bin_op, signed) {
312         (BinOp::Add, false) => fx.bcx.ins().select(has_overflow, max, val),
313         (BinOp::Sub, false) => fx.bcx.ins().select(has_overflow, min, val),
314         (BinOp::Add, true) => {
315             let rhs = rhs.load_scalar(fx);
316             let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
317             let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min);
318             fx.bcx.ins().select(has_overflow, sat_val, val)
319         }
320         (BinOp::Sub, true) => {
321             let rhs = rhs.load_scalar(fx);
322             let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
323             let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max);
324             fx.bcx.ins().select(has_overflow, sat_val, val)
325         }
326         _ => unreachable!(),
327     };
328
329     CValue::by_val(val, lhs.layout())
330 }
331
332 pub(crate) fn codegen_float_binop<'tcx>(
333     fx: &mut FunctionCx<'_, '_, 'tcx>,
334     bin_op: BinOp,
335     in_lhs: CValue<'tcx>,
336     in_rhs: CValue<'tcx>,
337 ) -> CValue<'tcx> {
338     assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
339
340     let lhs = in_lhs.load_scalar(fx);
341     let rhs = in_rhs.load_scalar(fx);
342
343     let b = fx.bcx.ins();
344     let res = match bin_op {
345         BinOp::Add => b.fadd(lhs, rhs),
346         BinOp::Sub => b.fsub(lhs, rhs),
347         BinOp::Mul => b.fmul(lhs, rhs),
348         BinOp::Div => b.fdiv(lhs, rhs),
349         BinOp::Rem => {
350             let name = match in_lhs.layout().ty.kind() {
351                 ty::Float(FloatTy::F32) => "fmodf",
352                 ty::Float(FloatTy::F64) => "fmod",
353                 _ => bug!(),
354             };
355             return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
356         }
357         BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
358             let fltcc = match bin_op {
359                 BinOp::Eq => FloatCC::Equal,
360                 BinOp::Lt => FloatCC::LessThan,
361                 BinOp::Le => FloatCC::LessThanOrEqual,
362                 BinOp::Ne => FloatCC::NotEqual,
363                 BinOp::Ge => FloatCC::GreaterThanOrEqual,
364                 BinOp::Gt => FloatCC::GreaterThan,
365                 _ => unreachable!(),
366             };
367             let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
368             return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
369         }
370         _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
371     };
372
373     CValue::by_val(res, in_lhs.layout())
374 }
375
376 pub(crate) fn codegen_ptr_binop<'tcx>(
377     fx: &mut FunctionCx<'_, '_, 'tcx>,
378     bin_op: BinOp,
379     in_lhs: CValue<'tcx>,
380     in_rhs: CValue<'tcx>,
381 ) -> CValue<'tcx> {
382     let is_thin_ptr = in_lhs
383         .layout()
384         .ty
385         .builtin_deref(true)
386         .map(|TypeAndMut { ty, mutbl: _ }| !has_ptr_meta(fx.tcx, ty))
387         .unwrap_or(true);
388
389     if is_thin_ptr {
390         match bin_op {
391             BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
392                 let lhs = in_lhs.load_scalar(fx);
393                 let rhs = in_rhs.load_scalar(fx);
394
395                 codegen_compare_bin_op(fx, bin_op, false, lhs, rhs)
396             }
397             BinOp::Offset => {
398                 let pointee_ty = in_lhs.layout().ty.builtin_deref(true).unwrap().ty;
399                 let (base, offset) = (in_lhs, in_rhs.load_scalar(fx));
400                 let pointee_size = fx.layout_of(pointee_ty).size.bytes();
401                 let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64);
402                 let base_val = base.load_scalar(fx);
403                 let res = fx.bcx.ins().iadd(base_val, ptr_diff);
404                 CValue::by_val(res, base.layout())
405             }
406             _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
407         }
408     } else {
409         let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx);
410         let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx);
411
412         let res = match bin_op {
413             BinOp::Eq => {
414                 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
415                 let extra_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_extra, rhs_extra);
416                 fx.bcx.ins().band(ptr_eq, extra_eq)
417             }
418             BinOp::Ne => {
419                 let ptr_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_ptr, rhs_ptr);
420                 let extra_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_extra, rhs_extra);
421                 fx.bcx.ins().bor(ptr_ne, extra_ne)
422             }
423             BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => {
424                 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
425
426                 let ptr_cmp =
427                     fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr);
428                 let extra_cmp = fx.bcx.ins().icmp(
429                     bin_op_to_intcc(bin_op, false).unwrap(),
430                     lhs_extra,
431                     rhs_extra,
432                 );
433
434                 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
435             }
436             _ => panic!("bin_op {:?} on ptr", bin_op),
437         };
438
439         CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
440     }
441 }
442
443 // In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
444 // For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
445 // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
446 // a float against itself. Only in case of NaN is it not equal to itself.
447 pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
448     let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
449     let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
450     let temp = fx.bcx.ins().select(a_ge_b, b, a);
451     fx.bcx.ins().select(a_is_nan, b, temp)
452 }
453
454 pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
455     let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
456     let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
457     let temp = fx.bcx.ins().select(a_le_b, b, a);
458     fx.bcx.ins().select(a_is_nan, b, temp)
459 }