mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
Implement saturating_{add, sub}
for non-native integer types
Updates their unsigned code paths to use the `Builder::gcc_` methods that automatically lower non-native integer operations to native ones. Also updates the signed code path of `saturating_add` to support non- native integer types. That of `saturating_sub` already supports this, so no major changes have been made.
This commit is contained in:
parent
837a4467bc
commit
00677e5159
@ -967,34 +967,55 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
}
|
||||
|
||||
fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
|
||||
let func = self.current_func.borrow().expect("func");
|
||||
|
||||
let result_type = lhs.get_type();
|
||||
if signed {
|
||||
// Algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||
let after_block = func.new_block("after");
|
||||
let func_name =
|
||||
match width {
|
||||
8 => "__builtin_add_overflow",
|
||||
16 => "__builtin_add_overflow",
|
||||
32 => "__builtin_sadd_overflow",
|
||||
64 => "__builtin_saddll_overflow",
|
||||
128 => "__builtin_add_overflow",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let overflow_func = self.context.get_builtin_function(func_name);
|
||||
let result_type = lhs.get_type();
|
||||
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||
let func = self.current_func.borrow().expect("func");
|
||||
let res = func.new_local(None, result_type, "saturating_sum");
|
||||
let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
|
||||
let supports_native_type = self.is_native_int_type(result_type);
|
||||
let overflow =
|
||||
if supports_native_type {
|
||||
let func_name =
|
||||
match width {
|
||||
8 => "__builtin_add_overflow",
|
||||
16 => "__builtin_add_overflow",
|
||||
32 => "__builtin_sadd_overflow",
|
||||
64 => "__builtin_saddll_overflow",
|
||||
128 => "__builtin_add_overflow",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let overflow_func = self.context.get_builtin_function(func_name);
|
||||
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None)
|
||||
}
|
||||
else {
|
||||
let func_name =
|
||||
match width {
|
||||
128 => "__rust_i128_addo",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let param_a = self.context.new_parameter(None, result_type, "a");
|
||||
let param_b = self.context.new_parameter(None, result_type, "b");
|
||||
let result_field = self.context.new_field(None, result_type, "result");
|
||||
let overflow_field = self.context.new_field(None, self.bool_type, "overflow");
|
||||
let return_type = self.context.new_struct_type(None, "result_overflow", &[result_field, overflow_field]);
|
||||
let func = self.context.new_function(None, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false);
|
||||
let result = self.context.new_call(None, func, &[lhs, rhs]);
|
||||
let overflow = result.access_field(None, overflow_field);
|
||||
let int_result = result.access_field(None, result_field);
|
||||
self.llbb().add_assignment(None, res, int_result);
|
||||
overflow
|
||||
};
|
||||
|
||||
let then_block = func.new_block("then");
|
||||
let after_block = func.new_block("after");
|
||||
|
||||
let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
|
||||
let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
|
||||
let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
|
||||
self.context.new_rvalue_from_int(unsigned_type, 0)
|
||||
);
|
||||
let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
|
||||
then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
|
||||
// Return `result_type`'s maximum or minimum value on overflow
|
||||
// NOTE: convert the type to unsigned to have an unsigned shift.
|
||||
let unsigned_type = result_type.to_unsigned(&self.cx);
|
||||
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
|
||||
let uint_max = self.gcc_not(self.gcc_int(unsigned_type, 0));
|
||||
let int_max = self.gcc_lshr(uint_max, self.gcc_int(unsigned_type, 1));
|
||||
then_block.add_assignment(None, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
|
||||
then_block.end_with_jump(None, after_block);
|
||||
|
||||
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
|
||||
@ -1006,20 +1027,20 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
res.to_rvalue()
|
||||
}
|
||||
else {
|
||||
assert!(!signed);
|
||||
// Algorithm from: http://locklessinc.com/articles/sat_arithmetic/
|
||||
let res = lhs + rhs;
|
||||
let res_type = res.get_type();
|
||||
let cond = self.context.new_comparison(None, ComparisonOp::LessThan, res, lhs);
|
||||
let value = self.context.new_unary_op(None, UnaryOp::Minus, res_type, self.context.new_cast(None, cond, res_type));
|
||||
res | value
|
||||
let res = self.gcc_add(lhs, rhs);
|
||||
let cond = self.gcc_icmp(IntPredicate::IntULT, res, lhs);
|
||||
let value = self.gcc_neg(self.gcc_int_cast(cond, result_type));
|
||||
self.gcc_or(res, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithm from: https://locklessinc.com/articles/sat_arithmetic/
|
||||
fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
|
||||
let result_type = lhs.get_type();
|
||||
if signed {
|
||||
// Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||
let result_type = lhs.get_type();
|
||||
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||
let func = self.current_func.borrow().expect("func");
|
||||
let res = func.new_local(None, result_type, "saturating_diff");
|
||||
let supports_native_type = self.is_native_int_type(result_type);
|
||||
@ -1059,6 +1080,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
let then_block = func.new_block("then");
|
||||
let after_block = func.new_block("after");
|
||||
|
||||
// Return `result_type`'s maximum or minimum value on overflow
|
||||
// NOTE: convert the type to unsigned to have an unsigned shift.
|
||||
let unsigned_type = result_type.to_unsigned(&self.cx);
|
||||
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
|
||||
@ -1076,11 +1098,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
res.to_rvalue()
|
||||
}
|
||||
else {
|
||||
let res = lhs - rhs;
|
||||
let comparison = self.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs);
|
||||
let comparison = self.context.new_cast(None, comparison, lhs.get_type());
|
||||
let unary_op = self.context.new_unary_op(None, UnaryOp::Minus, comparison.get_type(), comparison);
|
||||
self.and(res, unary_op)
|
||||
assert!(!signed);
|
||||
let res = self.gcc_sub(lhs, rhs);
|
||||
let comparison = self.gcc_icmp(IntPredicate::IntULE, res, lhs);
|
||||
let value = self.gcc_neg(self.gcc_int_cast(comparison, result_type));
|
||||
self.gcc_and(res, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user