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 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>,
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::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),
100 pub(crate) fn codegen_bool_binop<'tcx>(
101 fx: &mut FunctionCx<'_, '_, 'tcx>,
103 in_lhs: CValue<'tcx>,
104 in_rhs: CValue<'tcx>,
106 let lhs = in_lhs.load_scalar(fx);
107 let rhs = in_rhs.load_scalar(fx);
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),
118 CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
121 pub(crate) fn codegen_int_binop<'tcx>(
122 fx: &mut FunctionCx<'_, '_, 'tcx>,
124 in_lhs: CValue<'tcx>,
125 in_rhs: CValue<'tcx>,
127 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
131 "int binop requires lhs and rhs of same type"
135 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, false, in_lhs, in_rhs) {
139 let signed = type_sign(in_lhs.layout().ty);
141 let lhs = in_lhs.load_scalar(fx);
142 let rhs = in_rhs.load_scalar(fx);
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),
163 BinOp::BitXor => b.bxor(lhs, rhs),
164 BinOp::BitAnd => b.band(lhs, rhs),
165 BinOp::BitOr => b.bor(lhs, rhs),
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)
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));
175 fx.bcx.ins().sshr(lhs, actual_shift)
177 fx.bcx.ins().ushr(lhs, actual_shift)
180 // Compare binops handles by `codegen_binop`.
181 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
184 CValue::by_val(val, in_lhs.layout())
187 pub(crate) fn codegen_checked_int_binop<'tcx>(
188 fx: &mut FunctionCx<'_, '_, 'tcx>,
190 in_lhs: CValue<'tcx>,
191 in_rhs: CValue<'tcx>,
193 if bin_op != BinOp::Shl && bin_op != BinOp::Shr {
197 "checked int binop requires lhs and rhs of same type"
201 let lhs = in_lhs.load_scalar(fx);
202 let rhs = in_rhs.load_scalar(fx);
204 if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) {
208 let signed = type_sign(in_lhs.layout().ty);
210 let (res, has_overflow) = match bin_op {
212 /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs);
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)
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)
226 /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs);
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)
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)
240 let ty = fx.bcx.func.dfg.value_type(lhs);
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,
249 (1 << ty.bits()) - 1,
251 let val = fx.bcx.ins().ireduce(ty, val);
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);
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,
263 (1 << (ty.bits() - 1)) - 1,
265 let val = fx.bcx.ins().ireduce(ty, val);
266 (val, fx.bcx.ins().bor(has_underflow, has_overflow))
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)
274 // Based on LLVM's instruction sequence for compiling
275 // a.checked_mul(b).is_some() to riscv64gc:
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)
289 unreachable!("i128 should have been handled by codegen_i128::maybe_codegen")
291 _ => unreachable!("invalid non-integer type {}", ty),
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);
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)
311 fx.bcx.ins().sshr(lhs, actual_shift)
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);
318 _ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
321 let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow);
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)
327 pub(crate) fn codegen_float_binop<'tcx>(
328 fx: &mut FunctionCx<'_, '_, 'tcx>,
330 in_lhs: CValue<'tcx>,
331 in_rhs: CValue<'tcx>,
333 assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty);
335 let lhs = in_lhs.load_scalar(fx);
336 let rhs = in_rhs.load_scalar(fx);
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),
345 let name = match in_lhs.layout().ty.kind() {
346 ty::Float(FloatTy::F32) => "fmodf",
347 ty::Float(FloatTy::F64) => "fmod",
350 return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty);
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,
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));
366 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
369 CValue::by_val(res, in_lhs.layout())
372 pub(crate) fn codegen_ptr_binop<'tcx>(
373 fx: &mut FunctionCx<'_, '_, 'tcx>,
375 in_lhs: CValue<'tcx>,
376 in_rhs: CValue<'tcx>,
378 let is_thin_ptr = in_lhs
382 .map(|TypeAndMut { ty, mutbl: _ }| !has_ptr_meta(fx.tcx, ty))
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);
391 codegen_compare_bin_op(fx, bin_op, false, lhs, rhs)
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())
402 _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs),
405 let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx);
406 let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx);
408 let res = match bin_op {
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)
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)
419 BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => {
420 let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr);
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(),
430 fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp)
432 _ => panic!("bin_op {:?} on ptr", bin_op),
435 CValue::by_val(fx.bcx.ins().bint(types::I8, res), fx.layout_of(fx.tcx.types.bool))