Skip to content

Commit d69ada6

Browse files
authored
Merge pull request #156 from yvt/fix-int-ops
Implement `saturating_{add, sub}` for non-native integer types
2 parents 341b9f2 + a7a09d5 commit d69ada6

File tree

2 files changed

+374
-167
lines changed

2 files changed

+374
-167
lines changed

src/intrinsic/mod.rs

+55-35
Original file line numberDiff line numberDiff line change
@@ -967,34 +967,55 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
967967
}
968968

969969
fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
970-
let func = self.current_func.borrow().expect("func");
971-
970+
let result_type = lhs.get_type();
972971
if signed {
973-
// Algorithm from: https://stackoverflow.com/a/56531252/389119
974-
let after_block = func.new_block("after");
975-
let func_name =
976-
match width {
977-
8 => "__builtin_add_overflow",
978-
16 => "__builtin_add_overflow",
979-
32 => "__builtin_sadd_overflow",
980-
64 => "__builtin_saddll_overflow",
981-
128 => "__builtin_add_overflow",
982-
_ => unreachable!(),
983-
};
984-
let overflow_func = self.context.get_builtin_function(func_name);
985-
let result_type = lhs.get_type();
972+
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
973+
let func = self.current_func.borrow().expect("func");
986974
let res = func.new_local(None, result_type, "saturating_sum");
987-
let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
975+
let supports_native_type = self.is_native_int_type(result_type);
976+
let overflow =
977+
if supports_native_type {
978+
let func_name =
979+
match width {
980+
8 => "__builtin_add_overflow",
981+
16 => "__builtin_add_overflow",
982+
32 => "__builtin_sadd_overflow",
983+
64 => "__builtin_saddll_overflow",
984+
128 => "__builtin_add_overflow",
985+
_ => unreachable!(),
986+
};
987+
let overflow_func = self.context.get_builtin_function(func_name);
988+
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None)
989+
}
990+
else {
991+
let func_name =
992+
match width {
993+
128 => "__rust_i128_addo",
994+
_ => unreachable!(),
995+
};
996+
let param_a = self.context.new_parameter(None, result_type, "a");
997+
let param_b = self.context.new_parameter(None, result_type, "b");
998+
let result_field = self.context.new_field(None, result_type, "result");
999+
let overflow_field = self.context.new_field(None, self.bool_type, "overflow");
1000+
let return_type = self.context.new_struct_type(None, "result_overflow", &[result_field, overflow_field]);
1001+
let func = self.context.new_function(None, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false);
1002+
let result = self.context.new_call(None, func, &[lhs, rhs]);
1003+
let overflow = result.access_field(None, overflow_field);
1004+
let int_result = result.access_field(None, result_field);
1005+
self.llbb().add_assignment(None, res, int_result);
1006+
overflow
1007+
};
9881008

9891009
let then_block = func.new_block("then");
1010+
let after_block = func.new_block("after");
9901011

991-
let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
992-
let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
993-
let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
994-
self.context.new_rvalue_from_int(unsigned_type, 0)
995-
);
996-
let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
997-
then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
1012+
// Return `result_type`'s maximum or minimum value on overflow
1013+
// NOTE: convert the type to unsigned to have an unsigned shift.
1014+
let unsigned_type = result_type.to_unsigned(&self.cx);
1015+
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
1016+
let uint_max = self.gcc_not(self.gcc_int(unsigned_type, 0));
1017+
let int_max = self.gcc_lshr(uint_max, self.gcc_int(unsigned_type, 1));
1018+
then_block.add_assignment(None, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
9981019
then_block.end_with_jump(None, after_block);
9991020

10001021
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
@@ -1007,19 +1028,18 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
10071028
}
10081029
else {
10091030
// Algorithm from: http://locklessinc.com/articles/sat_arithmetic/
1010-
let res = lhs + rhs;
1011-
let res_type = res.get_type();
1012-
let cond = self.context.new_comparison(None, ComparisonOp::LessThan, res, lhs);
1013-
let value = self.context.new_unary_op(None, UnaryOp::Minus, res_type, self.context.new_cast(None, cond, res_type));
1014-
res | value
1031+
let res = self.gcc_add(lhs, rhs);
1032+
let cond = self.gcc_icmp(IntPredicate::IntULT, res, lhs);
1033+
let value = self.gcc_neg(self.gcc_int_cast(cond, result_type));
1034+
self.gcc_or(res, value)
10151035
}
10161036
}
10171037

10181038
// Algorithm from: https://locklessinc.com/articles/sat_arithmetic/
10191039
fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
1040+
let result_type = lhs.get_type();
10201041
if signed {
1021-
// Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
1022-
let result_type = lhs.get_type();
1042+
// Based on algorithm from: https://stackoverflow.com/a/56531252/389119
10231043
let func = self.current_func.borrow().expect("func");
10241044
let res = func.new_local(None, result_type, "saturating_diff");
10251045
let supports_native_type = self.is_native_int_type(result_type);
@@ -1059,6 +1079,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
10591079
let then_block = func.new_block("then");
10601080
let after_block = func.new_block("after");
10611081

1082+
// Return `result_type`'s maximum or minimum value on overflow
10621083
// NOTE: convert the type to unsigned to have an unsigned shift.
10631084
let unsigned_type = result_type.to_unsigned(&self.cx);
10641085
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
@@ -1076,11 +1097,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
10761097
res.to_rvalue()
10771098
}
10781099
else {
1079-
let res = lhs - rhs;
1080-
let comparison = self.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs);
1081-
let comparison = self.context.new_cast(None, comparison, lhs.get_type());
1082-
let unary_op = self.context.new_unary_op(None, UnaryOp::Minus, comparison.get_type(), comparison);
1083-
self.and(res, unary_op)
1100+
let res = self.gcc_sub(lhs, rhs);
1101+
let comparison = self.gcc_icmp(IntPredicate::IntULE, res, lhs);
1102+
let value = self.gcc_neg(self.gcc_int_cast(comparison, result_type));
1103+
self.gcc_and(res, value)
10841104
}
10851105
}
10861106
}

0 commit comments

Comments
 (0)