1 //! Replaces 128-bit operators with lang item calls where necessary
2
3 use cranelift_codegen::ir::ArgumentPurpose;
4
5 use crate::prelude::*;
6
maybe_codegen<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, lhs: CValue<'tcx>, rhs: CValue<'tcx>, ) -> Option<CValue<'tcx>>7 pub(crate) fn maybe_codegen<'tcx>(
8 fx: &mut FunctionCx<'_, '_, 'tcx>,
9 bin_op: BinOp,
10 lhs: CValue<'tcx>,
11 rhs: CValue<'tcx>,
12 ) -> Option<CValue<'tcx>> {
13 if lhs.layout().ty != fx.tcx.types.u128
14 && lhs.layout().ty != fx.tcx.types.i128
15 && rhs.layout().ty != fx.tcx.types.u128
16 && rhs.layout().ty != fx.tcx.types.i128
17 {
18 return None;
19 }
20
21 let is_signed = type_sign(lhs.layout().ty);
22
23 match bin_op {
24 BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None,
25 BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None,
26 BinOp::Mul | BinOp::MulUnchecked => {
27 let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
28 let ret_val = fx.lib_call(
29 "__multi3",
30 vec![AbiParam::new(types::I128), AbiParam::new(types::I128)],
31 vec![AbiParam::new(types::I128)],
32 &args,
33 )[0];
34 Some(CValue::by_val(
35 ret_val,
36 fx.layout_of(if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }),
37 ))
38 }
39 BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
40 BinOp::Div | BinOp::Rem => {
41 let name = match (bin_op, is_signed) {
42 (BinOp::Div, false) => "__udivti3",
43 (BinOp::Div, true) => "__divti3",
44 (BinOp::Rem, false) => "__umodti3",
45 (BinOp::Rem, true) => "__modti3",
46 _ => unreachable!(),
47 };
48 if fx.tcx.sess.target.is_like_windows {
49 let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
50 let ret = fx.lib_call(
51 name,
52 vec![AbiParam::new(types::I128), AbiParam::new(types::I128)],
53 vec![AbiParam::new(types::I64X2)],
54 &args,
55 )[0];
56 // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128
57 let ret_place = CPlace::new_stack_slot(fx, lhs.layout());
58 ret_place.to_ptr().store(fx, ret, MemFlags::trusted());
59 Some(ret_place.to_cvalue(fx))
60 } else {
61 let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
62 let ret_val = fx.lib_call(
63 name,
64 vec![AbiParam::new(types::I128), AbiParam::new(types::I128)],
65 vec![AbiParam::new(types::I128)],
66 &args,
67 )[0];
68 Some(CValue::by_val(ret_val, lhs.layout()))
69 }
70 }
71 BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
72 BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
73 }
74 }
75
maybe_codegen_checked<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, lhs: CValue<'tcx>, rhs: CValue<'tcx>, ) -> Option<CValue<'tcx>>76 pub(crate) fn maybe_codegen_checked<'tcx>(
77 fx: &mut FunctionCx<'_, '_, 'tcx>,
78 bin_op: BinOp,
79 lhs: CValue<'tcx>,
80 rhs: CValue<'tcx>,
81 ) -> Option<CValue<'tcx>> {
82 if lhs.layout().ty != fx.tcx.types.u128
83 && lhs.layout().ty != fx.tcx.types.i128
84 && rhs.layout().ty != fx.tcx.types.u128
85 && rhs.layout().ty != fx.tcx.types.i128
86 {
87 return None;
88 }
89
90 let is_signed = type_sign(lhs.layout().ty);
91
92 match bin_op {
93 BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(),
94 BinOp::Mul if is_signed => {
95 let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
96 let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32));
97 let lhs = lhs.load_scalar(fx);
98 let rhs = rhs.load_scalar(fx);
99 let oflow_ptr = oflow.to_ptr().get_addr(fx);
100 let res = fx.lib_call_unadjusted(
101 "__muloti4",
102 vec![
103 AbiParam::new(types::I128),
104 AbiParam::new(types::I128),
105 AbiParam::new(fx.pointer_type),
106 ],
107 vec![AbiParam::new(types::I128)],
108 &[lhs, rhs, oflow_ptr],
109 )[0];
110 let oflow = oflow.to_cvalue(fx).load_scalar(fx);
111 let oflow = fx.bcx.ins().ireduce(types::I8, oflow);
112 Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty)))
113 }
114 BinOp::Add | BinOp::Sub | BinOp::Mul => {
115 let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
116 let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty));
117 let param_types = vec![
118 AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn),
119 AbiParam::new(types::I128),
120 AbiParam::new(types::I128),
121 ];
122 let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)];
123 let name = match (bin_op, is_signed) {
124 (BinOp::Add, false) => "__rust_u128_addo",
125 (BinOp::Add, true) => "__rust_i128_addo",
126 (BinOp::Sub, false) => "__rust_u128_subo",
127 (BinOp::Sub, true) => "__rust_i128_subo",
128 (BinOp::Mul, false) => "__rust_u128_mulo",
129 _ => unreachable!(),
130 };
131 fx.lib_call(name, param_types, vec![], &args);
132 Some(out_place.to_cvalue(fx))
133 }
134 BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
135 BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
136 BinOp::Div | BinOp::Rem => unreachable!(),
137 BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
138 BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
139 }
140 }
141