1 //! Various operations on integer and floating-point numbers
5 pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option<IntCC> {
21 UnsignedLessThanOrEqual
27 SignedGreaterThanOrEqual
29 UnsignedGreaterThanOrEqual
43 fn codegen_compare_bin_op<'tcx>(
44 fx: &mut FunctionCx<'_, '_, '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 CValue::by_val(val, fx.layout_of(fx.tcx.types.bool))
55 pub(crate) fn codegen_binop<'tcx>(
56 fx: &mut FunctionCx<'_, '_, 'tcx>,
62 BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
63 match in_lhs.layout().ty.kind() {
64 ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => {
65 let signed = type_sign(in_lhs.layout().ty);
66 let lhs = in_lhs.load_scalar(fx);
67 let rhs = in_rhs.load_scalar(fx);
69 return codegen_compare_bin_op(fx, bin_op, signed, lhs, rhs);
77 match in_lhs.layout().ty.kind() {
78 ty::Bool => crate::num::codegen_bool_binop(fx, bin_op, in_lhs, in_rhs),
79 ty::Uint(_) | ty::Int(_) => crate::num::codegen_int_binop(fx, bin_op, in_lhs, in_rhs),
80 ty::Float(_) => crate::num::codegen_float_binop(fx, bin_op, in_lhs, in_rhs),
81 ty::RawPtr(..) | ty::FnPtr(..) => crate::num::codegen_ptr_binop(fx, bin_op, in_lhs, in_rhs),
82 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
86 pub(crate) fn codegen_bool_binop<'tcx>(
87 fx: &mut FunctionCx<'_, '_, 'tcx>,
92 let lhs = in_lhs.load_scalar(fx);
93 let rhs = in_rhs.load_scalar(fx);
96 let res = match bin_op {
97 BinOp::BitXor => b.bxor(lhs, rhs),
98 BinOp::BitAnd => b.band(lhs, rhs),
99 BinOp::BitOr => b.bor(lhs, rhs),
100 // Compare binops handles by `codegen_binop`.
101 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
104 CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
107 pub(crate) fn codegen_int_binop<'tcx>(
108 fx: &mut FunctionCx<'_, '_, 'tcx>,
110 in_lhs: CValue<'tcx>,
111 in_rhs: CValue<'tcx>,
113 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
117 "int binop requires lhs and rhs of same type"
121 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, false, in_lhs, in_rhs) {
125 let signed = type_sign(in_lhs.layout().ty);
127 let lhs = in_lhs.load_scalar(fx);
128 let rhs = in_rhs.load_scalar(fx);
130 let b = fx.bcx.ins();
131 let val = match bin_op {
132 BinOp::Add => b.iadd(lhs, rhs),
133 BinOp::Sub => b.isub(lhs, rhs),
134 BinOp::Mul => b.imul(lhs, rhs),
149 BinOp::BitXor => b.bxor(lhs, rhs),
150 BinOp::BitAnd => b.band(lhs, rhs),
151 BinOp::BitOr => b.bor(lhs, rhs),
152 BinOp::Shl => b.ishl(lhs, rhs),
160 // Compare binops handles by `codegen_binop`.
161 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
164 CValue::by_val(val, in_lhs.layout())
167 pub(crate) fn codegen_checked_int_binop<'tcx>(
168 fx: &mut FunctionCx<'_, '_, 'tcx>,
170 in_lhs: CValue<'tcx>,
171 in_rhs: CValue<'tcx>,
173 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
177 "checked int binop requires lhs and rhs of same type"
181 let lhs = in_lhs.load_scalar(fx);
182 let rhs = in_rhs.load_scalar(fx);
184 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) {
188 let signed = type_sign(in_lhs.layout().ty);
190 let (res, has_overflow) = match bin_op {
192 /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs);
194 // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16
195 let val = fx.bcx.ins().iadd(lhs, rhs);
196 let has_overflow = if !signed {
197 fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs)
199 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
200 let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs);
201 fx.bcx.ins().bxor(rhs_is_negative, slt)
206 /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs);
208 // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16
209 let val = fx.bcx.ins().isub(lhs, rhs);
210 let has_overflow = if !signed {
211 fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs)
213 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
214 let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs);
215 fx.bcx.ins().bxor(rhs_is_negative, sgt)
220 let ty = fx.bcx.func.dfg.value_type(lhs);
222 types::I8 | types::I16 | types::I32 if !signed => {
223 let lhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), lhs);
224 let rhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), rhs);
225 let val = fx.bcx.ins().imul(lhs, rhs);
226 let has_overflow = fx.bcx.ins().icmp_imm(
227 IntCC::UnsignedGreaterThan,
229 (1 << ty.bits()) - 1,
231 let val = fx.bcx.ins().ireduce(ty, val);
234 types::I8 | types::I16 | types::I32 if signed => {
235 let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs);
236 let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs);
237 let val = fx.bcx.ins().imul(lhs, rhs);
239 fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1)));
240 let has_overflow = fx.bcx.ins().icmp_imm(
241 IntCC::SignedGreaterThan,
243 (1 << (ty.bits() - 1)) - 1,
245 let val = fx.bcx.ins().ireduce(ty, val);
246 (val, fx.bcx.ins().bor(has_underflow, has_overflow))
249 let val = fx.bcx.ins().imul(lhs, rhs);
250 let has_overflow = if !signed {
251 let val_hi = fx.bcx.ins().umulhi(lhs, rhs);
252 fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0)
254 // Based on LLVM's instruction sequence for compiling
255 // a.checked_mul(b).is_some() to riscv64gc:
261 let val_hi = fx.bcx.ins().smulhi(lhs, rhs);
262 let val_sign = fx.bcx.ins().sshr_imm(val, i64::from(ty.bits() - 1));
263 let xor = fx.bcx.ins().bxor(val_hi, val_sign);
264 fx.bcx.ins().icmp_imm(IntCC::NotEqual, xor, 0)
269 unreachable!("i128 should have been handled by codegen_i128::maybe_codegen")
271 _ => unreachable!("invalid non-integer type {}", ty),
275 let val = fx.bcx.ins().ishl(lhs, rhs);
276 let ty = fx.bcx.func.dfg.value_type(val);
277 let max_shift = i64::from(ty.bits()) - 1;
278 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
283 if !signed { fx.bcx.ins().ushr(lhs, rhs) } else { fx.bcx.ins().sshr(lhs, rhs) };
284 let ty = fx.bcx.func.dfg.value_type(val);
285 let max_shift = i64::from(ty.bits()) - 1;
286 let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
289 _ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
292 let out_layout = fx.layout_of(fx.tcx.mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()));
293 CValue::by_val_pair(res, has_overflow, out_layout)
296 pub(crate) fn codegen_saturating_int_binop<'tcx>(
297 fx: &mut FunctionCx<'_, '_, 'tcx>,
302 assert_eq!(lhs.layout().ty, rhs.layout().ty);
304 let signed = type_sign(lhs.layout().ty);
305 let clif_ty = fx.clif_type(lhs.layout().ty).unwrap();
306 let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed);
308 let checked_res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs);
309 let (val, has_overflow) = checked_res.load_scalar_pair(fx);
311 let val = match (bin_op, signed) {
312 (BinOp::Add, false) => fx.bcx.ins().select(has_overflow, max, val),
313 (BinOp::Sub, false) => fx.bcx.ins().select(has_overflow, min, val),
314 (BinOp::Add, true) => {
315 let rhs = rhs.load_scalar(fx);
316 let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
317 let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min);
318 fx.bcx.ins().select(has_overflow, sat_val, val)
320 (BinOp::Sub, true) => {
321 let rhs = rhs.load_scalar(fx);
322 let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
323 let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max);
324 fx.bcx.ins().select(has_overflow, sat_val, val)
329 CValue::by_val(val, lhs.layout())
332 pub(crate) fn codegen_float_binop<'tcx>(
333 fx: &mut FunctionCx<'_, '_, 'tcx>,
335 in_lhs: CValue<'tcx>,
336 in_rhs: CValue<'tcx>,
338 assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
340 let lhs = in_lhs.load_scalar(fx);
341 let rhs = in_rhs.load_scalar(fx);
343 let b = fx.bcx.ins();
344 let res = match bin_op {
345 BinOp::Add => b.fadd(lhs, rhs),
346 BinOp::Sub => b.fsub(lhs, rhs),
347 BinOp::Mul => b.fmul(lhs, rhs),
348 BinOp::Div => b.fdiv(lhs, rhs),
350 let name = match in_lhs.layout().ty.kind() {
351 ty::Float(FloatTy::F32) => "fmodf",
352 ty::Float(FloatTy::F64) => "fmod",
355 return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
357 BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
358 let fltcc = match bin_op {
359 BinOp::Eq => FloatCC::Equal,
360 BinOp::Lt => FloatCC::LessThan,
361 BinOp::Le => FloatCC::LessThanOrEqual,
362 BinOp::Ne => FloatCC::NotEqual,
363 BinOp::Ge => FloatCC::GreaterThanOrEqual,
364 BinOp::Gt => FloatCC::GreaterThan,
367 let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
368 return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
370 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
373 CValue::by_val(res, in_lhs.layout())
376 pub(crate) fn codegen_ptr_binop<'tcx>(
377 fx: &mut FunctionCx<'_, '_, 'tcx>,
379 in_lhs: CValue<'tcx>,
380 in_rhs: CValue<'tcx>,
382 let is_thin_ptr = in_lhs
386 .map(|TypeAndMut { ty, mutbl: _ }| !has_ptr_meta(fx.tcx, ty))
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);
395 codegen_compare_bin_op(fx, bin_op, false, lhs, rhs)
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 CValue::by_val(res, base.layout())
406 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
409 let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx);
410 let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx);
412 let res = match bin_op {
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)
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)
423 BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => {
424 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
427 fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr);
428 let extra_cmp = fx.bcx.ins().icmp(
429 bin_op_to_intcc(bin_op, false).unwrap(),
434 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
436 _ => panic!("bin_op {:?} on ptr", bin_op),
439 CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
443 // In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
444 // For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
445 // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
446 // a float against itself. Only in case of NaN is it not equal to itself.
447 pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
448 let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
449 let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
450 let temp = fx.bcx.ins().select(a_ge_b, b, a);
451 fx.bcx.ins().select(a_is_nan, b, temp)
454 pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
455 let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
456 let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
457 let temp = fx.bcx.ins().select(a_le_b, b, a);
458 fx.bcx.ins().select(a_is_nan, b, temp)