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