]> git.lizzy.rs Git - rust.git/blob - src/num.rs
Merge pull request #1056 from bjorn3/misc_fixes
[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(IntCC::UnsignedGreaterThan, val, (1 << ty.bits()) - 1);
257                     let val = fx.bcx.ins().ireduce(ty, val);
258                     (val, has_overflow)
259                 }
260                 types::I8 | types::I16 | types::I32 if signed => {
261                     let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs);
262                     let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs);
263                     let val = fx.bcx.ins().imul(lhs, rhs);
264                     let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1)));
265                     let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, (1 << (ty.bits() - 1)) - 1);
266                     let val = fx.bcx.ins().ireduce(ty, val);
267                     (val, fx.bcx.ins().bor(has_underflow, has_overflow))
268                 }
269                 types::I64 => {
270                     //let val = fx.easy_call("__mulodi4", &[lhs, rhs, overflow_ptr], types::I64);
271                     let val = fx.bcx.ins().imul(lhs, rhs);
272                     let has_overflow = if !signed {
273                         let val_hi = fx.bcx.ins().umulhi(lhs, rhs);
274                         fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0)
275                     } else {
276                         let val_hi = fx.bcx.ins().smulhi(lhs, rhs);
277                         let not_all_zero = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);
278                         let not_all_ones = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, u64::try_from((1u128 << ty.bits()) - 1).unwrap() as i64);
279                         fx.bcx.ins().band(not_all_zero, not_all_ones)
280                     };
281                     (val, has_overflow)
282                 }
283                 types::I128 => unreachable!("i128 should have been handled by codegen_i128::maybe_codegen"),
284                 _ => unreachable!("invalid non-integer type {}", ty),
285             }
286         }
287         BinOp::Shl => {
288             let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
289             let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
290             let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
291             let val = fx.bcx.ins().ishl(lhs, actual_shift);
292             let ty = fx.bcx.func.dfg.value_type(val);
293             let max_shift = i64::from(ty.bits()) - 1;
294             let has_overflow =
295                 fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
296             (val, has_overflow)
297         }
298         BinOp::Shr => {
299             let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
300             let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
301             let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
302             let val = if !signed {
303                 fx.bcx.ins().ushr(lhs, actual_shift)
304             } else {
305                 fx.bcx.ins().sshr(lhs, actual_shift)
306             };
307             let ty = fx.bcx.func.dfg.value_type(val);
308             let max_shift = i64::from(ty.bits()) - 1;
309             let has_overflow =
310                 fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
311             (val, has_overflow)
312         }
313         _ => bug!(
314             "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}",
315             bin_op,
316             in_lhs,
317             in_rhs
318         ),
319     };
320
321     let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow);
322
323     // FIXME directly write to result place instead
324     let out_place = CPlace::new_stack_slot(
325         fx,
326         fx.layout_of(fx.tcx.mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter())),
327     );
328     let out_layout = out_place.layout();
329     out_place.write_cvalue(fx, CValue::by_val_pair(res, has_overflow, out_layout));
330
331     out_place.to_cvalue(fx)
332 }
333
334 pub(crate) fn trans_float_binop<'tcx>(
335     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
336     bin_op: BinOp,
337     in_lhs: CValue<'tcx>,
338     in_rhs: CValue<'tcx>,
339 ) -> CValue<'tcx> {
340     assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
341
342     let lhs = in_lhs.load_scalar(fx);
343     let rhs = in_rhs.load_scalar(fx);
344
345     let b = fx.bcx.ins();
346     let res = match bin_op {
347         BinOp::Add => b.fadd(lhs, rhs),
348         BinOp::Sub => b.fsub(lhs, rhs),
349         BinOp::Mul => b.fmul(lhs, rhs),
350         BinOp::Div => b.fdiv(lhs, rhs),
351         BinOp::Rem => {
352             let name = match in_lhs.layout().ty.kind {
353                 ty::Float(FloatTy::F32) => "fmodf",
354                 ty::Float(FloatTy::F64) => "fmod",
355                 _ => bug!(),
356             };
357             return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
358         }
359         BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
360             let fltcc = match bin_op {
361                 BinOp::Eq => FloatCC::Equal,
362                 BinOp::Lt => FloatCC::LessThan,
363                 BinOp::Le => FloatCC::LessThanOrEqual,
364                 BinOp::Ne => FloatCC::NotEqual,
365                 BinOp::Ge => FloatCC::GreaterThanOrEqual,
366                 BinOp::Gt => FloatCC::GreaterThan,
367                 _ => unreachable!(),
368             };
369             let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
370             let val = fx.bcx.ins().bint(types::I8, val);
371             return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
372         }
373         _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
374     };
375
376     CValue::by_val(res, in_lhs.layout())
377 }
378
379 pub(crate) fn trans_ptr_binop<'tcx>(
380     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
381     bin_op: BinOp,
382     in_lhs: CValue<'tcx>,
383     in_rhs: CValue<'tcx>,
384 ) -> CValue<'tcx> {
385     let is_thin_ptr = in_lhs.layout().ty.builtin_deref(true).map(|TypeAndMut { ty, mutbl: _}| {
386         !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                 return 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                 return 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
428                         .ins()
429                         .icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr);
430                 let extra_cmp = fx.bcx.ins().icmp(
431                     bin_op_to_intcc(bin_op, false).unwrap(),
432                     lhs_extra,
433                     rhs_extra,
434                 );
435
436                 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
437             }
438             _ => panic!("bin_op {:?} on ptr", bin_op),
439         };
440
441         CValue::by_val(
442             fx.bcx.ins().bint(types::I8, res),
443             fx.layout_of(fx.tcx.types.bool),
444         )
445     }
446 }