]> git.lizzy.rs Git - rust.git/blob - src/num.rs
Format code using 'cargo fmt'
[rust.git] / src / num.rs
1 use crate::prelude::*;
2
3 pub 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 = codegen_icmp(fx, 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 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.sty {
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.sty == fx.tcx.types.i8.sty
70                             || in_lhs.layout().ty.sty == fx.tcx.types.i16.sty)
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.sty {
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         _ => unimplemented!(
95             "{:?}({:?}, {:?})",
96             bin_op,
97             in_lhs.layout().ty,
98             in_rhs.layout().ty
99         ),
100     }
101 }
102
103 pub 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 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 rhs = clif_intcast(fx, rhs, lhs_ty, false);
172             fx.bcx.ins().ishl(lhs, rhs)
173         }
174         BinOp::Shr => {
175             let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
176             let rhs = clif_intcast(fx, rhs, lhs_ty, false);
177             if signed {
178                 fx.bcx.ins().sshr(lhs, rhs)
179             } else {
180                 fx.bcx.ins().ushr(lhs, rhs)
181             }
182         }
183         // Compare binops handles by `codegen_binop`.
184         _ => unreachable!(
185             "{:?}({:?}, {:?})",
186             bin_op,
187             in_lhs.layout().ty,
188             in_rhs.layout().ty
189         ),
190     };
191
192     CValue::by_val(val, in_lhs.layout())
193 }
194
195 pub fn trans_checked_int_binop<'tcx>(
196     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
197     bin_op: BinOp,
198     in_lhs: CValue<'tcx>,
199     in_rhs: CValue<'tcx>,
200 ) -> CValue<'tcx> {
201     if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
202         assert_eq!(
203             in_lhs.layout().ty,
204             in_rhs.layout().ty,
205             "checked int binop requires lhs and rhs of same type"
206         );
207     }
208
209     let lhs = in_lhs.load_scalar(fx);
210     let rhs = in_rhs.load_scalar(fx);
211
212     if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) {
213         return res;
214     }
215
216     let signed = type_sign(in_lhs.layout().ty);
217
218     let (res, has_overflow) = match bin_op {
219         BinOp::Add => {
220             /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs);
221             (val, c_out)*/
222             // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16
223             let val = fx.bcx.ins().iadd(lhs, rhs);
224             let has_overflow = if !signed {
225                 fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs)
226             } else {
227                 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
228                 let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs);
229                 fx.bcx.ins().bxor(rhs_is_negative, slt)
230             };
231             (val, has_overflow)
232         }
233         BinOp::Sub => {
234             /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs);
235             (val, b_out)*/
236             // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16
237             let val = fx.bcx.ins().isub(lhs, rhs);
238             let has_overflow = if !signed {
239                 fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs)
240             } else {
241                 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
242                 let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs);
243                 fx.bcx.ins().bxor(rhs_is_negative, sgt)
244             };
245             (val, has_overflow)
246         }
247         BinOp::Mul => {
248             let val = fx.bcx.ins().imul(lhs, rhs);
249             /*let val_hi = if !signed {
250                 fx.bcx.ins().umulhi(lhs, rhs)
251             } else {
252                 fx.bcx.ins().smulhi(lhs, rhs)
253             };
254             let has_overflow = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);*/
255             // TODO: check for overflow
256             let has_overflow = fx.bcx.ins().bconst(types::B1, false);
257             (val, has_overflow)
258         }
259         BinOp::Shl => {
260             let val = fx.bcx.ins().ishl(lhs, rhs);
261             // TODO: check for overflow
262             let has_overflow = fx.bcx.ins().bconst(types::B1, false);
263             (val, has_overflow)
264         }
265         BinOp::Shr => {
266             let val = if !signed {
267                 fx.bcx.ins().ushr(lhs, rhs)
268             } else {
269                 fx.bcx.ins().sshr(lhs, rhs)
270             };
271             // TODO: check for overflow
272             let has_overflow = fx.bcx.ins().bconst(types::B1, false);
273             (val, has_overflow)
274         }
275         _ => bug!(
276             "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}",
277             bin_op,
278             in_lhs,
279             in_rhs
280         ),
281     };
282
283     let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow);
284     let out_place = CPlace::new_stack_slot(
285         fx,
286         fx.tcx
287             .mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()),
288     );
289     let out_layout = out_place.layout();
290     out_place.write_cvalue(fx, CValue::by_val_pair(res, has_overflow, out_layout));
291
292     out_place.to_cvalue(fx)
293 }
294
295 pub fn trans_float_binop<'tcx>(
296     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
297     bin_op: BinOp,
298     in_lhs: CValue<'tcx>,
299     in_rhs: CValue<'tcx>,
300 ) -> CValue<'tcx> {
301     assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
302
303     let lhs = in_lhs.load_scalar(fx);
304     let rhs = in_rhs.load_scalar(fx);
305
306     let b = fx.bcx.ins();
307     let res = match bin_op {
308         BinOp::Add => b.fadd(lhs, rhs),
309         BinOp::Sub => b.fsub(lhs, rhs),
310         BinOp::Mul => b.fmul(lhs, rhs),
311         BinOp::Div => b.fdiv(lhs, rhs),
312         BinOp::Rem => {
313             let name = match in_lhs.layout().ty.sty {
314                 ty::Float(FloatTy::F32) => "fmodf",
315                 ty::Float(FloatTy::F64) => "fmod",
316                 _ => bug!(),
317             };
318             return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
319         }
320         BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
321             let fltcc = match bin_op {
322                 BinOp::Eq => FloatCC::Equal,
323                 BinOp::Lt => FloatCC::LessThan,
324                 BinOp::Le => FloatCC::LessThanOrEqual,
325                 BinOp::Ne => FloatCC::NotEqual,
326                 BinOp::Ge => FloatCC::GreaterThanOrEqual,
327                 BinOp::Gt => FloatCC::GreaterThan,
328                 _ => unreachable!(),
329             };
330             let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
331             let val = fx.bcx.ins().bint(types::I8, val);
332             return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
333         }
334         _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
335     };
336
337     CValue::by_val(res, in_lhs.layout())
338 }
339
340 pub fn trans_ptr_binop<'tcx>(
341     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
342     bin_op: BinOp,
343     in_lhs: CValue<'tcx>,
344     in_rhs: CValue<'tcx>,
345 ) -> CValue<'tcx> {
346     let not_fat = match in_lhs.layout().ty.sty {
347         ty::RawPtr(TypeAndMut { ty, mutbl: _ }) => {
348             ty.is_sized(fx.tcx.at(DUMMY_SP), ParamEnv::reveal_all())
349         }
350         ty::FnPtr(..) => true,
351         _ => bug!("trans_ptr_binop on non ptr"),
352     };
353     if not_fat {
354         match bin_op {
355             BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
356                 let lhs = in_lhs.load_scalar(fx);
357                 let rhs = in_rhs.load_scalar(fx);
358
359                 return codegen_compare_bin_op(fx, bin_op, false, lhs, rhs);
360             }
361             BinOp::Offset => {
362                 let (base, offset) = (in_lhs, in_rhs.load_scalar(fx));
363                 let pointee_ty = base.layout().ty.builtin_deref(true).unwrap().ty;
364                 let pointee_size = fx.layout_of(pointee_ty).size.bytes();
365                 let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64);
366                 let base_val = base.load_scalar(fx);
367                 let res = fx.bcx.ins().iadd(base_val, ptr_diff);
368                 return CValue::by_val(res, base.layout());
369             }
370             _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
371         };
372     } else {
373         let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx);
374         let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx);
375
376         let res = match bin_op {
377             BinOp::Eq => {
378                 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
379                 let extra_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_extra, rhs_extra);
380                 fx.bcx.ins().band(ptr_eq, extra_eq)
381             }
382             BinOp::Ne => {
383                 let ptr_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_ptr, rhs_ptr);
384                 let extra_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_extra, rhs_extra);
385                 fx.bcx.ins().bor(ptr_ne, extra_ne)
386             }
387             BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => {
388                 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
389
390                 let ptr_cmp =
391                     fx.bcx
392                         .ins()
393                         .icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr);
394                 let extra_cmp = fx.bcx.ins().icmp(
395                     bin_op_to_intcc(bin_op, false).unwrap(),
396                     lhs_extra,
397                     rhs_extra,
398                 );
399
400                 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
401             }
402             _ => panic!("bin_op {:?} on ptr", bin_op),
403         };
404
405         CValue::by_val(
406             fx.bcx.ins().bint(types::I8, res),
407             fx.layout_of(fx.tcx.types.bool),
408         )
409     }
410 }