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, impl Module>,
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))
56 pub(crate) fn codegen_binop<'tcx>(
57 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
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);
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())
74 // FIXME(CraneStation/cranelift#896) icmp_imm.i8/i16 with eq/ne for signed ints is implemented wrong.
76 fx.bcx.ins().sextend(types::I32, lhs),
77 fx.bcx.ins().sextend(types::I32, rhs),
83 return codegen_compare_bin_op(fx, bin_op, signed, lhs, rhs);
91 match in_lhs.layout().ty.kind() {
92 ty::Bool => crate::num::trans_bool_binop(fx, bin_op, in_lhs, in_rhs),
93 ty::Uint(_) | ty::Int(_) => crate::num::trans_int_binop(fx, bin_op, in_lhs, in_rhs),
94 ty::Float(_) => crate::num::trans_float_binop(fx, bin_op, in_lhs, in_rhs),
95 ty::RawPtr(..) | ty::FnPtr(..) => crate::num::trans_ptr_binop(fx, bin_op, in_lhs, in_rhs),
105 pub(crate) fn trans_bool_binop<'tcx>(
106 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
108 in_lhs: CValue<'tcx>,
109 in_rhs: CValue<'tcx>,
111 let lhs = in_lhs.load_scalar(fx);
112 let rhs = in_rhs.load_scalar(fx);
114 let b = fx.bcx.ins();
115 let res = match bin_op {
116 BinOp::BitXor => b.bxor(lhs, rhs),
117 BinOp::BitAnd => b.band(lhs, rhs),
118 BinOp::BitOr => b.bor(lhs, rhs),
119 // Compare binops handles by `codegen_binop`.
120 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
123 CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
126 pub(crate) fn trans_int_binop<'tcx>(
127 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
129 in_lhs: CValue<'tcx>,
130 in_rhs: CValue<'tcx>,
132 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
136 "int binop requires lhs and rhs of same type"
140 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, false, in_lhs, in_rhs) {
144 let signed = type_sign(in_lhs.layout().ty);
146 let lhs = in_lhs.load_scalar(fx);
147 let rhs = in_rhs.load_scalar(fx);
149 let b = fx.bcx.ins();
150 let val = match bin_op {
151 BinOp::Add => b.iadd(lhs, rhs),
152 BinOp::Sub => b.isub(lhs, rhs),
153 BinOp::Mul => b.imul(lhs, rhs),
168 BinOp::BitXor => b.bxor(lhs, rhs),
169 BinOp::BitAnd => b.band(lhs, rhs),
170 BinOp::BitOr => b.bor(lhs, rhs),
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 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
175 fx.bcx.ins().ishl(lhs, actual_shift)
178 let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
179 let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
180 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
182 fx.bcx.ins().sshr(lhs, actual_shift)
184 fx.bcx.ins().ushr(lhs, actual_shift)
187 // Compare binops handles by `codegen_binop`.
196 CValue::by_val(val, in_lhs.layout())
199 pub(crate) fn trans_checked_int_binop<'tcx>(
200 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
202 in_lhs: CValue<'tcx>,
203 in_rhs: CValue<'tcx>,
205 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
209 "checked int binop requires lhs and rhs of same type"
213 let lhs = in_lhs.load_scalar(fx);
214 let rhs = in_rhs.load_scalar(fx);
216 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) {
220 let signed = type_sign(in_lhs.layout().ty);
222 let (res, has_overflow) = match bin_op {
224 /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs);
226 // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16
227 let val = fx.bcx.ins().iadd(lhs, rhs);
228 let has_overflow = if !signed {
229 fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs)
231 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
232 let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs);
233 fx.bcx.ins().bxor(rhs_is_negative, slt)
238 /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs);
240 // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16
241 let val = fx.bcx.ins().isub(lhs, rhs);
242 let has_overflow = if !signed {
243 fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs)
245 let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0);
246 let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs);
247 fx.bcx.ins().bxor(rhs_is_negative, sgt)
252 let ty = fx.bcx.func.dfg.value_type(lhs);
254 types::I8 | types::I16 | types::I32 if !signed => {
255 let lhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), lhs);
256 let rhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), rhs);
257 let val = fx.bcx.ins().imul(lhs, rhs);
258 let has_overflow = fx.bcx.ins().icmp_imm(
259 IntCC::UnsignedGreaterThan,
261 (1 << ty.bits()) - 1,
263 let val = fx.bcx.ins().ireduce(ty, val);
266 types::I8 | types::I16 | types::I32 if signed => {
267 let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs);
268 let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs);
269 let val = fx.bcx.ins().imul(lhs, rhs);
273 .icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1)));
274 let has_overflow = fx.bcx.ins().icmp_imm(
275 IntCC::SignedGreaterThan,
277 (1 << (ty.bits() - 1)) - 1,
279 let val = fx.bcx.ins().ireduce(ty, val);
280 (val, fx.bcx.ins().bor(has_underflow, has_overflow))
283 //let val = fx.easy_call("__mulodi4", &[lhs, rhs, overflow_ptr], types::I64);
284 let val = fx.bcx.ins().imul(lhs, rhs);
285 let has_overflow = if !signed {
286 let val_hi = fx.bcx.ins().umulhi(lhs, rhs);
287 fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0)
289 let val_hi = fx.bcx.ins().smulhi(lhs, rhs);
290 let not_all_zero = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);
291 let not_all_ones = fx.bcx.ins().icmp_imm(
294 u64::try_from((1u128 << ty.bits()) - 1).unwrap() as i64,
296 fx.bcx.ins().band(not_all_zero, not_all_ones)
301 unreachable!("i128 should have been handled by codegen_i128::maybe_codegen")
303 _ => unreachable!("invalid non-integer type {}", ty),
307 let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
308 let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
309 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
310 let val = fx.bcx.ins().ishl(lhs, actual_shift);
311 let ty = fx.bcx.func.dfg.value_type(val);
312 let max_shift = i64::from(ty.bits()) - 1;
313 let has_overflow = fx
316 .icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
320 let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
321 let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
322 let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
323 let val = if !signed {
324 fx.bcx.ins().ushr(lhs, actual_shift)
326 fx.bcx.ins().sshr(lhs, actual_shift)
328 let ty = fx.bcx.func.dfg.value_type(val);
329 let max_shift = i64::from(ty.bits()) - 1;
330 let has_overflow = fx
333 .icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift);
337 "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}",
344 let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow);
346 // FIXME directly write to result place instead
347 let out_place = CPlace::new_stack_slot(
351 .mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()),
354 let out_layout = out_place.layout();
355 out_place.write_cvalue(fx, CValue::by_val_pair(res, has_overflow, out_layout));
357 out_place.to_cvalue(fx)
360 pub(crate) fn trans_float_binop<'tcx>(
361 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
363 in_lhs: CValue<'tcx>,
364 in_rhs: CValue<'tcx>,
366 assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
368 let lhs = in_lhs.load_scalar(fx);
369 let rhs = in_rhs.load_scalar(fx);
371 let b = fx.bcx.ins();
372 let res = match bin_op {
373 BinOp::Add => b.fadd(lhs, rhs),
374 BinOp::Sub => b.fsub(lhs, rhs),
375 BinOp::Mul => b.fmul(lhs, rhs),
376 BinOp::Div => b.fdiv(lhs, rhs),
378 let name = match in_lhs.layout().ty.kind() {
379 ty::Float(FloatTy::F32) => "fmodf",
380 ty::Float(FloatTy::F64) => "fmod",
383 return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
385 BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
386 let fltcc = match bin_op {
387 BinOp::Eq => FloatCC::Equal,
388 BinOp::Lt => FloatCC::LessThan,
389 BinOp::Le => FloatCC::LessThanOrEqual,
390 BinOp::Ne => FloatCC::NotEqual,
391 BinOp::Ge => FloatCC::GreaterThanOrEqual,
392 BinOp::Gt => FloatCC::GreaterThan,
395 let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs);
396 let val = fx.bcx.ins().bint(types::I8, val);
397 return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool));
399 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
402 CValue::by_val(res, in_lhs.layout())
405 pub(crate) fn trans_ptr_binop<'tcx>(
406 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
408 in_lhs: CValue<'tcx>,
409 in_rhs: CValue<'tcx>,
411 let is_thin_ptr = in_lhs
415 .map(|TypeAndMut { ty, mutbl: _ }| !has_ptr_meta(fx.tcx, ty))
420 BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
421 let lhs = in_lhs.load_scalar(fx);
422 let rhs = in_rhs.load_scalar(fx);
424 return codegen_compare_bin_op(fx, bin_op, false, lhs, rhs);
427 let pointee_ty = in_lhs.layout().ty.builtin_deref(true).unwrap().ty;
428 let (base, offset) = (in_lhs, in_rhs.load_scalar(fx));
429 let pointee_size = fx.layout_of(pointee_ty).size.bytes();
430 let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64);
431 let base_val = base.load_scalar(fx);
432 let res = fx.bcx.ins().iadd(base_val, ptr_diff);
433 return CValue::by_val(res, base.layout());
435 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
438 let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx);
439 let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx);
441 let res = match bin_op {
443 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
444 let extra_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_extra, rhs_extra);
445 fx.bcx.ins().band(ptr_eq, extra_eq)
448 let ptr_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_ptr, rhs_ptr);
449 let extra_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_extra, rhs_extra);
450 fx.bcx.ins().bor(ptr_ne, extra_ne)
452 BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => {
453 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
458 .icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr);
459 let extra_cmp = fx.bcx.ins().icmp(
460 bin_op_to_intcc(bin_op, false).unwrap(),
465 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
467 _ => panic!("bin_op {:?} on ptr", bin_op),
471 fx.bcx.ins().bint(types::I8, res),
472 fx.layout_of(fx.tcx.types.bool),