Skip to content

Conversation

@FractalFir
Copy link
Contributor

Currently, ashr & lshr share the same implementation, and the shift type is guessed based on the sign of the type.

This is incorrect W.R.T. cg_ssa semantics(type don't have signs, operations do), and has caused silent miscompilations in the past(cg_ssa requested a logcal shift, but cg_gcc inserted an arithmetic one). This bug was pretty rare, but was still a miscompilation.

This PR fixes that bug, by introducing a new parameter(ShiftKind) to gcc_shr(renamed from gcc_lshr). This allows both shift kinds to still share an implementation, while being very explicit about the requested shift.

Copy link
Contributor

@antoyo antoyo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making this PR!

let b_size = b_type.get_size();
match a_size.cmp(&b_size) {
std::cmp::Ordering::Less => {
let a = self.context.new_cast(self.location, a, b_type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a debug_assert!() that checks that the sign of a and b is the same here?

}
std::cmp::Ordering::Equal => a >> b,
std::cmp::Ordering::Greater => {
let b = self.context.new_cast(self.location, b, a_type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here as well.

Comment on lines -86 to +105
std::cmp::Ordering::Less => {
let a = self.context.new_cast(self.location, a, b_type);
a >> b
}
std::cmp::Ordering::Equal => a >> b,
std::cmp::Ordering::Greater => {
let b = self.context.new_cast(self.location, b, a_type);
a >> b
}
let (a, a_type) = match (shift_kind, a_type.is_signed(self.cx)) {
(ShiftKind::Logical, true) => {
let a_type = a_type.to_unsigned(self.cx);
(self.gcc_int_cast(a, a_type), a_type)
}
(ShiftKind::Logical, false) => (a, a_type),
(ShiftKind::Arithmetic, true) => (a, a_type),
(ShiftKind::Arithmetic, false) => {
let a_type = a_type.to_signed(self.cx);
(self.gcc_int_cast(a, a_type), a_type)
}
};
let (b, b_type) = match (shift_kind, b_type.is_signed(self.cx)) {
(ShiftKind::Logical, true) => {
let b_type = b_type.to_unsigned(self.cx);
(self.gcc_int_cast(b, b_type), b_type)
}
(ShiftKind::Logical, false) => (b, b_type),
(ShiftKind::Arithmetic, true) => (b, b_type),
(ShiftKind::Arithmetic, false) => {
let b_type = b_type.to_signed(self.cx);
(self.gcc_int_cast(b, b_type), b_type)
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a comment to explain that this casts to unsigned if needed to do a logical shift and to signed if needed to do an arithmetic shift?

use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp};
use rustc_middle::ty::{self, Ty};
use rustc_target::callconv::{ArgAbi, ArgAttributes, FnAbi, PassMode};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be easy to add a test for this fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants