Skip to content

Commit

Permalink
Use a C-safe return type for __rust_[ui]128_* overflowing intrinsics
Browse files Browse the repository at this point in the history
Combined with [1], this will change the overflowing multiplication
operations to return an `extern "C"`-safe type.

Link: rust-lang/compiler-builtins#735 [1]
  • Loading branch information
tgross35 committed Jan 11, 2025
1 parent a57449f commit e319a57
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 71 deletions.
18 changes: 11 additions & 7 deletions compiler/rustc_codegen_cranelift/src/codegen_i128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,25 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(),
BinOp::Add | BinOp::Sub => None,
BinOp::Mul => {
let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty));
let oflow_out_place = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32));
let param_types = vec![
AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn),
AbiParam::new(types::I128),
AbiParam::new(types::I128),
AbiParam::special(fx.pointer_type, ArgumentPurpose::Normal),
];
let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)];
fx.lib_call(
let args =
[lhs.load_scalar(fx), rhs.load_scalar(fx), oflow_out_place.to_ptr().get_addr(fx)];
let ret = fx.lib_call(
if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" },
param_types,
vec![],
vec![AbiParam::new(types::I128)],
&args,
);
Some(out_place.to_cvalue(fx))
let mul = ret[0];
let oflow = oflow_out_place.to_cvalue(fx).load_scalar(fx);
let oflow = clif_intcast(fx, oflow, types::I8, false);
let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]));
Some(CValue::by_val_pair(mul, oflow, layout))
}
BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/compiler_builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ builtin_functions! {
fn __divti3(n: i128, d: i128) -> i128;
fn __umodti3(n: u128, d: u128) -> u128;
fn __modti3(n: i128, d: i128) -> i128;
fn __rust_u128_mulo(a: u128, b: u128) -> (u128, bool);
fn __rust_u128_mulo(a: u128, b: u128, oflow: &mut i32) -> u128;

// floats
fn __floattisf(i: i128) -> f32;
Expand Down
112 changes: 49 additions & 63 deletions compiler/rustc_codegen_gcc/src/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,36 +322,26 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
},
}
} else {
match new_kind {
Int(I128) | Uint(U128) => {
let func_name = match oop {
OverflowOp::Add => match new_kind {
Int(I128) => "__rust_i128_addo",
Uint(U128) => "__rust_u128_addo",
_ => unreachable!(),
},
OverflowOp::Sub => match new_kind {
Int(I128) => "__rust_i128_subo",
Uint(U128) => "__rust_u128_subo",
_ => unreachable!(),
},
OverflowOp::Mul => match new_kind {
Int(I128) => "__rust_i128_mulo", // TODO(antoyo): use __muloti4d instead?
Uint(U128) => "__rust_u128_mulo",
_ => unreachable!(),
},
};
return self.operation_with_overflow(func_name, lhs, rhs);
}
_ => match oop {
OverflowOp::Mul => match new_kind {
Int(I32) => "__mulosi4",
Int(I64) => "__mulodi4",
_ => unreachable!(),
},
_ => unimplemented!("overflow operation for {:?}", new_kind),
let func_name = match oop {
OverflowOp::Add => match new_kind {
Int(I128) => "__rust_i128_addo",
Uint(U128) => "__rust_u128_addo",
_ => unreachable!(),
},
}
OverflowOp::Sub => match new_kind {
Int(I128) => "__rust_i128_subo",
Uint(U128) => "__rust_u128_subo",
_ => unreachable!(),
},
OverflowOp::Mul => match new_kind {
Int(I32) => "__mulosi4",
Int(I64) => "__mulodi4",
Int(I128) => "__rust_i128_mulo", // TODO(antoyo): use __muloti4d instead?
Uint(U128) => "__rust_u128_mulo",
_ => unreachable!(),
},
};
return self.operation_with_overflow(func_name, lhs, rhs);
};

let intrinsic = self.context.get_builtin_function(name);
Expand All @@ -364,6 +354,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
(res.dereference(self.location).to_rvalue(), overflow)
}

/// `i128` overflow operations with a `fn(i128, i128, &mut i32) -> i128` signature.
pub fn operation_with_overflow(
&self,
func_name: &str,
Expand All @@ -374,70 +365,65 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let b_type = rhs.get_type();
debug_assert!(a_type.dyncast_array().is_some());
debug_assert!(b_type.dyncast_array().is_some());
let oflow_type = self.bool_type;
let oflow_param_type = oflow_type.make_pointer();
let res_type = a_type;

let oflow_value = self.current_func().new_local(self.location, oflow_type, "overflow");
let oflow_addr = oflow_value.get_address(self.location);

let param_a = self.context.new_parameter(self.location, a_type, "a");
let param_b = self.context.new_parameter(self.location, b_type, "b");
let result_field = self.context.new_field(self.location, a_type, "result");
let overflow_field = self.context.new_field(self.location, self.bool_type, "overflow");
let param_oflow = self.context.new_parameter(self.location, oflow_param_type, "oflow");

let ret_ty = Ty::new_tup(self.tcx, &[self.tcx.types.i128, self.tcx.types.bool]);
let res_ty = self.tcx.types.i128;
let layout = self
.tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ret_ty))
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(res_ty))
.unwrap();

let arg_abi = ArgAbi { layout, mode: PassMode::Direct(ArgAttributes::new()) };
let mut fn_abi = FnAbi {
args: vec![arg_abi.clone(), arg_abi.clone()].into_boxed_slice(),
args: vec![arg_abi.clone(), arg_abi.clone(), arg_abi.clone()].into_boxed_slice(),
ret: arg_abi,
c_variadic: false,
fixed_count: 2,
fixed_count: 3,
conv: Conv::C,
can_unwind: false,
};
fn_abi.adjust_for_foreign_abi(self.cx, spec::abi::Abi::C { unwind: false }).unwrap();

let indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });

let return_type = self
.context
.new_struct_type(self.location, "result_overflow", &[result_field, overflow_field]);
let result = if indirect {
let return_value =
self.current_func().new_local(self.location, return_type.as_type(), "return_value");
let return_param_type = return_type.as_type().make_pointer();
let return_param =
self.context.new_parameter(self.location, return_param_type, "return_value");
let ret_indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });

if ret_indirect {
let res_value = self.current_func().new_local(self.location, res_type, "result_value");
let res_addr = res_value.get_address(self.location);
let res_param_type = res_type.make_pointer();
let param_res = self.context.new_parameter(self.location, res_param_type, "result");

let func = self.context.new_function(
self.location,
FunctionType::Extern,
self.type_void(),
&[return_param, param_a, param_b],
&[param_res, param_a, param_b, param_oflow],
func_name,
false,
);
self.llbb().add_eval(
self.location,
self.context.new_call(self.location, func, &[
return_value.get_address(self.location),
lhs,
rhs,
]),
);
return_value.to_rvalue()
let _void =
self.context.new_call(self.location, func, &[res_addr, lhs, rhs, oflow_addr]);
(res_value.to_rvalue(), oflow_value.to_rvalue())
} else {
let func = self.context.new_function(
self.location,
FunctionType::Extern,
return_type.as_type(),
&[param_a, param_b],
res_type,
&[param_a, param_b, param_oflow],
func_name,
false,
);
self.context.new_call(self.location, func, &[lhs, rhs])
};
let overflow = result.access_field(self.location, overflow_field);
let int_result = result.access_field(self.location, result_field);
(int_result, overflow)
let res = self.context.new_call(self.location, func, &[lhs, rhs, oflow_addr]);
(res, oflow_value.to_rvalue())
}
}

pub fn gcc_icmp(
Expand Down

0 comments on commit e319a57

Please sign in to comment.