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
::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
),
105 pub(crate) fn codegen_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 codegen_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 codegen_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
.bcx
.ins().imul(lhs
, rhs
);
284 let has_overflow
= if !signed
{
285 let val_hi
= fx
.bcx
.ins().umulhi(lhs
, rhs
);
286 fx
.bcx
.ins().icmp_imm(IntCC
::NotEqual
, val_hi
, 0)
288 let val_hi
= fx
.bcx
.ins().smulhi(lhs
, rhs
);
289 let not_all_zero
= fx
.bcx
.ins().icmp_imm(IntCC
::NotEqual
, val_hi
, 0);
290 let not_all_ones
= fx
.bcx
.ins().icmp_imm(
293 u64::try_from((1u128 << ty
.bits()) - 1).unwrap() as i64,
295 fx
.bcx
.ins().band(not_all_zero
, not_all_ones
)
300 unreachable
!("i128 should have been handled by codegen_i128::maybe_codegen")
302 _
=> unreachable
!("invalid non-integer type {}", ty
),
306 let lhs_ty
= fx
.bcx
.func
.dfg
.value_type(lhs
);
307 let actual_shift
= fx
.bcx
.ins().band_imm(rhs
, i64::from(lhs_ty
.bits() - 1));
308 let actual_shift
= clif_intcast(fx
, actual_shift
, types
::I8
, false);
309 let val
= fx
.bcx
.ins().ishl(lhs
, actual_shift
);
310 let ty
= fx
.bcx
.func
.dfg
.value_type(val
);
311 let max_shift
= i64::from(ty
.bits()) - 1;
312 let has_overflow
= fx
315 .icmp_imm(IntCC
::UnsignedGreaterThan
, rhs
, max_shift
);
319 let lhs_ty
= fx
.bcx
.func
.dfg
.value_type(lhs
);
320 let actual_shift
= fx
.bcx
.ins().band_imm(rhs
, i64::from(lhs_ty
.bits() - 1));
321 let actual_shift
= clif_intcast(fx
, actual_shift
, types
::I8
, false);
322 let val
= if !signed
{
323 fx
.bcx
.ins().ushr(lhs
, actual_shift
)
325 fx
.bcx
.ins().sshr(lhs
, actual_shift
)
327 let ty
= fx
.bcx
.func
.dfg
.value_type(val
);
328 let max_shift
= i64::from(ty
.bits()) - 1;
329 let has_overflow
= fx
332 .icmp_imm(IntCC
::UnsignedGreaterThan
, rhs
, max_shift
);
336 "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}",
343 let has_overflow
= fx
.bcx
.ins().bint(types
::I8
, has_overflow
);
345 // FIXME directly write to result place instead
346 let out_place
= CPlace
::new_stack_slot(
350 .mk_tup([in_lhs
.layout().ty
, fx
.tcx
.types
.bool
].iter()),
353 let out_layout
= out_place
.layout();
354 out_place
.write_cvalue(fx
, CValue
::by_val_pair(res
, has_overflow
, out_layout
));
356 out_place
.to_cvalue(fx
)
359 pub(crate) fn codegen_float_binop
<'tcx
>(
360 fx
: &mut FunctionCx
<'_
, 'tcx
, impl Module
>,
362 in_lhs
: CValue
<'tcx
>,
363 in_rhs
: CValue
<'tcx
>,
365 assert_eq
!(in_lhs
.layout().ty
, in_rhs
.layout().ty
);
367 let lhs
= in_lhs
.load_scalar(fx
);
368 let rhs
= in_rhs
.load_scalar(fx
);
370 let b
= fx
.bcx
.ins();
371 let res
= match bin_op
{
372 BinOp
::Add
=> b
.fadd(lhs
, rhs
),
373 BinOp
::Sub
=> b
.fsub(lhs
, rhs
),
374 BinOp
::Mul
=> b
.fmul(lhs
, rhs
),
375 BinOp
::Div
=> b
.fdiv(lhs
, rhs
),
377 let name
= match in_lhs
.layout().ty
.kind() {
378 ty
::Float(FloatTy
::F32
) => "fmodf",
379 ty
::Float(FloatTy
::F64
) => "fmod",
382 return fx
.easy_call(name
, &[in_lhs
, in_rhs
], in_lhs
.layout().ty
);
384 BinOp
::Eq
| BinOp
::Lt
| BinOp
::Le
| BinOp
::Ne
| BinOp
::Ge
| BinOp
::Gt
=> {
385 let fltcc
= match bin_op
{
386 BinOp
::Eq
=> FloatCC
::Equal
,
387 BinOp
::Lt
=> FloatCC
::LessThan
,
388 BinOp
::Le
=> FloatCC
::LessThanOrEqual
,
389 BinOp
::Ne
=> FloatCC
::NotEqual
,
390 BinOp
::Ge
=> FloatCC
::GreaterThanOrEqual
,
391 BinOp
::Gt
=> FloatCC
::GreaterThan
,
394 let val
= fx
.bcx
.ins().fcmp(fltcc
, lhs
, rhs
);
395 let val
= fx
.bcx
.ins().bint(types
::I8
, val
);
396 return CValue
::by_val(val
, fx
.layout_of(fx
.tcx
.types
.bool
));
398 _
=> unreachable
!("{:?}({:?}, {:?})", bin_op
, in_lhs
, in_rhs
),
401 CValue
::by_val(res
, in_lhs
.layout())
404 pub(crate) fn codegen_ptr_binop
<'tcx
>(
405 fx
: &mut FunctionCx
<'_
, 'tcx
, impl Module
>,
407 in_lhs
: CValue
<'tcx
>,
408 in_rhs
: CValue
<'tcx
>,
410 let is_thin_ptr
= in_lhs
414 .map(|TypeAndMut { ty, mutbl: _ }
| !has_ptr_meta(fx
.tcx
, ty
))
419 BinOp
::Eq
| BinOp
::Lt
| BinOp
::Le
| BinOp
::Ne
| BinOp
::Ge
| BinOp
::Gt
=> {
420 let lhs
= in_lhs
.load_scalar(fx
);
421 let rhs
= in_rhs
.load_scalar(fx
);
423 return codegen_compare_bin_op(fx
, bin_op
, false, lhs
, rhs
);
426 let pointee_ty
= in_lhs
.layout().ty
.builtin_deref(true).unwrap().ty
;
427 let (base
, offset
) = (in_lhs
, in_rhs
.load_scalar(fx
));
428 let pointee_size
= fx
.layout_of(pointee_ty
).size
.bytes();
429 let ptr_diff
= fx
.bcx
.ins().imul_imm(offset
, pointee_size
as i64);
430 let base_val
= base
.load_scalar(fx
);
431 let res
= fx
.bcx
.ins().iadd(base_val
, ptr_diff
);
432 return CValue
::by_val(res
, base
.layout());
434 _
=> unreachable
!("{:?}({:?}, {:?})", bin_op
, in_lhs
, in_rhs
),
437 let (lhs_ptr
, lhs_extra
) = in_lhs
.load_scalar_pair(fx
);
438 let (rhs_ptr
, rhs_extra
) = in_rhs
.load_scalar_pair(fx
);
440 let res
= match bin_op
{
442 let ptr_eq
= fx
.bcx
.ins().icmp(IntCC
::Equal
, lhs_ptr
, rhs_ptr
);
443 let extra_eq
= fx
.bcx
.ins().icmp(IntCC
::Equal
, lhs_extra
, rhs_extra
);
444 fx
.bcx
.ins().band(ptr_eq
, extra_eq
)
447 let ptr_ne
= fx
.bcx
.ins().icmp(IntCC
::NotEqual
, lhs_ptr
, rhs_ptr
);
448 let extra_ne
= fx
.bcx
.ins().icmp(IntCC
::NotEqual
, lhs_extra
, rhs_extra
);
449 fx
.bcx
.ins().bor(ptr_ne
, extra_ne
)
451 BinOp
::Lt
| BinOp
::Le
| BinOp
::Ge
| BinOp
::Gt
=> {
452 let ptr_eq
= fx
.bcx
.ins().icmp(IntCC
::Equal
, lhs_ptr
, rhs_ptr
);
457 .icmp(bin_op_to_intcc(bin_op
, false).unwrap(), lhs_ptr
, rhs_ptr
);
458 let extra_cmp
= fx
.bcx
.ins().icmp(
459 bin_op_to_intcc(bin_op
, false).unwrap(),
464 fx
.bcx
.ins().select(ptr_eq
, extra_cmp
, ptr_cmp
)
466 _
=> panic
!("bin_op {:?} on ptr", bin_op
),
470 fx
.bcx
.ins().bint(types
::I8
, res
),
471 fx
.layout_of(fx
.tcx
.types
.bool
),