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> {
|
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 {
|
if signed {
|
||||||
// Algorithm from: https://stackoverflow.com/a/56531252/389119
|
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||||
let after_block = func.new_block("after");
|
let func = self.current_func.borrow().expect("func");
|
||||||
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();
|
|
||||||
let res = func.new_local(None, result_type, "saturating_sum");
|
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 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);
|
// Return `result_type`'s maximum or minimum value on overflow
|
||||||
let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
|
// NOTE: convert the type to unsigned to have an unsigned shift.
|
||||||
let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
|
let unsigned_type = result_type.to_unsigned(&self.cx);
|
||||||
self.context.new_rvalue_from_int(unsigned_type, 0)
|
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 = uint_max >> self.context.new_rvalue_one(unsigned_type);
|
let int_max = self.gcc_lshr(uint_max, self.gcc_int(unsigned_type, 1));
|
||||||
then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
|
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);
|
then_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
self.llbb().end_with_conditional(None, overflow, then_block, 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()
|
res.to_rvalue()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
assert!(!signed);
|
||||||
// Algorithm from: http://locklessinc.com/articles/sat_arithmetic/
|
// Algorithm from: http://locklessinc.com/articles/sat_arithmetic/
|
||||||
let res = lhs + rhs;
|
let res = self.gcc_add(lhs, rhs);
|
||||||
let res_type = res.get_type();
|
let cond = self.gcc_icmp(IntPredicate::IntULT, res, lhs);
|
||||||
let cond = self.context.new_comparison(None, ComparisonOp::LessThan, res, lhs);
|
let value = self.gcc_neg(self.gcc_int_cast(cond, result_type));
|
||||||
let value = self.context.new_unary_op(None, UnaryOp::Minus, res_type, self.context.new_cast(None, cond, res_type));
|
self.gcc_or(res, value)
|
||||||
res | value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Algorithm from: https://locklessinc.com/articles/sat_arithmetic/
|
// 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> {
|
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 {
|
if signed {
|
||||||
// Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||||
let result_type = lhs.get_type();
|
|
||||||
let func = self.current_func.borrow().expect("func");
|
let func = self.current_func.borrow().expect("func");
|
||||||
let res = func.new_local(None, result_type, "saturating_diff");
|
let res = func.new_local(None, result_type, "saturating_diff");
|
||||||
let supports_native_type = self.is_native_int_type(result_type);
|
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 then_block = func.new_block("then");
|
||||||
let after_block = func.new_block("after");
|
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.
|
// NOTE: convert the type to unsigned to have an unsigned shift.
|
||||||
let unsigned_type = result_type.to_unsigned(&self.cx);
|
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 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()
|
res.to_rvalue()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let res = lhs - rhs;
|
assert!(!signed);
|
||||||
let comparison = self.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs);
|
let res = self.gcc_sub(lhs, rhs);
|
||||||
let comparison = self.context.new_cast(None, comparison, lhs.get_type());
|
let comparison = self.gcc_icmp(IntPredicate::IntULE, res, lhs);
|
||||||
let unary_op = self.context.new_unary_op(None, UnaryOp::Minus, comparison.get_type(), comparison);
|
let value = self.gcc_neg(self.gcc_int_cast(comparison, result_type));
|
||||||
self.and(res, unary_op)
|
self.gcc_and(res, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user