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