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