]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_codegen_cranelift/src/num.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.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 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
170 fx.bcx.ins().ishl(lhs, actual_shift)
171 }
172 BinOp::Shr => {
173 let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
174 let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
175 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
176 if signed {
177 fx.bcx.ins().sshr(lhs, actual_shift)
178 } else {
179 fx.bcx.ins().ushr(lhs, actual_shift)
180 }
181 }
182 // Compare binops handles by `codegen_binop`.
183 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
184 };
185
186 CValue::by_val(val, in_lhs.layout())
187 }
188
189 pub(crate) fn codegen_checked_int_binop<'tcx>(
190 fx: &mut FunctionCx<'_, '_, 'tcx>,
191 bin_op: BinOp,
192 in_lhs: CValue<'tcx>,
193 in_rhs: CValue<'tcx>,
194 ) -> CValue<'tcx> {
195 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
196 assert_eq!(
197 in_lhs.layout().ty,
198 in_rhs.layout().ty,
199 "checked int binop requires lhs and rhs of same type"
200 );
201 }
202
203 let lhs = in_lhs.load_scalar(fx);
204 let rhs = in_rhs.load_scalar(fx);
205
206 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) {
207 return res;
208 }
209
210 let signed = type_sign(in_lhs.layout().ty);
211
212 let (res, has_overflow) = match bin_op {
213 BinOp::Add => {
214 /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs);
215 (val, c_out)*/
216 // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16
217 let val = fx.bcx.ins().iadd(lhs, rhs);
218 let has_overflow = if !signed {
219 fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs)
220 } else {
221 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
222 let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs);
223 fx.bcx.ins().bxor(rhs_is_negative, slt)
224 };
225 (val, has_overflow)
226 }
227 BinOp::Sub => {
228 /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs);
229 (val, b_out)*/
230 // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16
231 let val = fx.bcx.ins().isub(lhs, rhs);
232 let has_overflow = if !signed {
233 fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs)
234 } else {
235 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
236 let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs);
237 fx.bcx.ins().bxor(rhs_is_negative, sgt)
238 };
239 (val, has_overflow)
240 }
241 BinOp::Mul => {
242 let ty = fx.bcx.func.dfg.value_type(lhs);
243 match ty {
244 types::I8 | types::I16 | types::I32 if !signed => {
245 let lhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), lhs);
246 let rhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), rhs);
247 let val = fx.bcx.ins().imul(lhs, rhs);
248 let has_overflow = fx.bcx.ins().icmp_imm(
249 IntCC::UnsignedGreaterThan,
250 val,
251 (1 << ty.bits()) - 1,
252 );
253 let val = fx.bcx.ins().ireduce(ty, val);
254 (val, has_overflow)
255 }
256 types::I8 | types::I16 | types::I32 if signed => {
257 let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs);
258 let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs);
259 let val = fx.bcx.ins().imul(lhs, rhs);
260 let has_underflow =
261 fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1)));
262 let has_overflow = fx.bcx.ins().icmp_imm(
263 IntCC::SignedGreaterThan,
264 val,
265 (1 << (ty.bits() - 1)) - 1,
266 );
267 let val = fx.bcx.ins().ireduce(ty, val);
268 (val, fx.bcx.ins().bor(has_underflow, has_overflow))
269 }
270 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(
279 IntCC::NotEqual,
280 val_hi,
281 u64::try_from((1u128 << ty.bits()) - 1).unwrap() as i64,
282 );
283 fx.bcx.ins().band(not_all_zero, not_all_ones)
284 };
285 (val, has_overflow)
286 }
287 types::I128 => {
288 unreachable!("i128 should have been handled by codegen_i128::maybe_codegen")
289 }
290 _ => unreachable!("invalid non-integer type {}", ty),
291 }
292 }
293 BinOp::Shl => {
294 let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
295 let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
296 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
297 let val = fx.bcx.ins().ishl(lhs, actual_shift);
298 let ty = fx.bcx.func.dfg.value_type(val);
299 let max_shift = i64::from(ty.bits()) - 1;
300 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
301 (val, has_overflow)
302 }
303 BinOp::Shr => {
304 let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
305 let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
306 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
307 let val = if !signed {
308 fx.bcx.ins().ushr(lhs, actual_shift)
309 } else {
310 fx.bcx.ins().sshr(lhs, actual_shift)
311 };
312 let ty = fx.bcx.func.dfg.value_type(val);
313 let max_shift = i64::from(ty.bits()) - 1;
314 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
315 (val, has_overflow)
316 }
317 _ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
318 };
319
320 let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow);
321
322 let out_layout = fx.layout_of(fx.tcx.mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()));
323 CValue::by_val_pair(res, has_overflow, out_layout)
324 }
325
326 pub(crate) fn codegen_float_binop<'tcx>(
327 fx: &mut FunctionCx<'_, '_, 'tcx>,
328 bin_op: BinOp,
329 in_lhs: CValue<'tcx>,
330 in_rhs: CValue<'tcx>,
331 ) -> CValue<'tcx> {
332 assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
333
334 let lhs = in_lhs.load_scalar(fx);
335 let rhs = in_rhs.load_scalar(fx);
336
337 let b = fx.bcx.ins();
338 let res = match bin_op {
339 BinOp::Add => b.fadd(lhs, rhs),
340 BinOp::Sub => b.fsub(lhs, rhs),
341 BinOp::Mul => b.fmul(lhs, rhs),
342 BinOp::Div => b.fdiv(lhs, rhs),
343 BinOp::Rem => {
344 let name = match in_lhs.layout().ty.kind() {
345 ty::Float(FloatTy::F32) => "fmodf",
346 ty::Float(FloatTy::F64) => "fmod",
347 _ => bug!(),
348 };
349 return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
350 }
351 BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
352 let fltcc = match bin_op {
353 BinOp::Eq => FloatCC::Equal,
354 BinOp::Lt => FloatCC::LessThan,
355 BinOp::Le => FloatCC::LessThanOrEqual,
356 BinOp::Ne => FloatCC::NotEqual,
357 BinOp::Ge => FloatCC::GreaterThanOrEqual,
358 BinOp::Gt => FloatCC::GreaterThan,
359 _ => unreachable!(),
360 };
361 let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
362 let val = fx.bcx.ins().bint(types::I8, val);
363 return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
364 }
365 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
366 };
367
368 CValue::by_val(res, in_lhs.layout())
369 }
370
371 pub(crate) fn codegen_ptr_binop<'tcx>(
372 fx: &mut FunctionCx<'_, '_, 'tcx>,
373 bin_op: BinOp,
374 in_lhs: CValue<'tcx>,
375 in_rhs: CValue<'tcx>,
376 ) -> CValue<'tcx> {
377 let is_thin_ptr = in_lhs
378 .layout()
379 .ty
380 .builtin_deref(true)
381 .map(|TypeAndMut { ty, mutbl: _ }| !has_ptr_meta(fx.tcx, ty))
382 .unwrap_or(true);
383
384 if is_thin_ptr {
385 match bin_op {
386 BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
387 let lhs = in_lhs.load_scalar(fx);
388 let rhs = in_rhs.load_scalar(fx);
389
390 return codegen_compare_bin_op(fx, bin_op, false, lhs, rhs);
391 }
392 BinOp::Offset => {
393 let pointee_ty = in_lhs.layout().ty.builtin_deref(true).unwrap().ty;
394 let (base, offset) = (in_lhs, in_rhs.load_scalar(fx));
395 let pointee_size = fx.layout_of(pointee_ty).size.bytes();
396 let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64);
397 let base_val = base.load_scalar(fx);
398 let res = fx.bcx.ins().iadd(base_val, ptr_diff);
399 return CValue::by_val(res, base.layout());
400 }
401 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
402 };
403 } else {
404 let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx);
405 let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx);
406
407 let res = match bin_op {
408 BinOp::Eq => {
409 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
410 let extra_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_extra, rhs_extra);
411 fx.bcx.ins().band(ptr_eq, extra_eq)
412 }
413 BinOp::Ne => {
414 let ptr_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_ptr, rhs_ptr);
415 let extra_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_extra, rhs_extra);
416 fx.bcx.ins().bor(ptr_ne, extra_ne)
417 }
418 BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => {
419 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
420
421 let ptr_cmp =
422 fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr);
423 let extra_cmp = fx.bcx.ins().icmp(
424 bin_op_to_intcc(bin_op, false).unwrap(),
425 lhs_extra,
426 rhs_extra,
427 );
428
429 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
430 }
431 _ => panic!("bin_op {:?} on ptr", bin_op),
432 };
433
434 CValue::by_val(fx.bcx.ins().bint(types::I8, res), fx.layout_of(fx.tcx.types.bool))
435 }
436 }