Skip to content

Conversation

@qinsoon
Copy link
Member

@qinsoon qinsoon commented Aug 26, 2024

This PR fixes some bugs in finding base references for internal references. Prominently two bugs:

  1. The way we find the last non zero bit was wrong. We used to to use trailing_zeroes and compare the result with the given range of the starting and ending bits. This was simply wrong. Now we do a mask first to extract the value between the starting and the ending bits, and then use leading_zeroes. Performance-wise, the new code has similar performnace.
  2. When we convert a metadata bit (address + bit offset) back to the data address, the metadata bit needs to be the start of the metadata value. If it is the middle of the metadata value, then the computed data address will be incorrect. This PR adds align_metadata_address.

This PR skips some tests for LOS in plans that do not use LOS.

@qinsoon qinsoon marked this pull request as ready for review August 27, 2024 01:58
@qinsoon qinsoon requested a review from wks August 27, 2024 02:03
Comment on lines 305 to 318
fn find_last_non_zero_bit_u8(value: u8, start: u8, end: u8) -> Option<u8> {
// TODO: Ideally we implement the function as a generic function.
// However Rust does not have a generic trait for integers yet. We can use
// third party traits or our own trait (such as MetadataValue), but it is cumbersome,
// and does not worth the efforts.
impl_find_last_non_zero_bit!(u8, value, start, end)
}

fn find_last_non_zero_bit_usize(value: usize, start: u8, end: u8) -> Option<u8> {
// TODO: Ideally we implement the function as a generic function.
// However Rust does not have a generic trait for integers yet. We can use
// third party traits or our own trait (such as MetadataValue), but it is cumbersome,
// and does not worth the efforts.
impl_find_last_non_zero_bit!(usize, value, start, end)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually we already depend on the num-traits crate, and it has the needed traits for bit operations, including CheckedShl. Macros perform fewer compile-time checks, and may surprise us in unexpected ways (such as missing type casts and unexpected type coercion). Writing a generic function is safer and more readable. It is a bit harder to write, but not that bad. Here is one version that works. (Note that I replaced leading_zeroes >= <$int_ty>::BITS with a simple comparison against zero (.is_zero()))

fn find_last_non_zero_bit_in_t<T>(value: T, start: u8, end: u8) -> Option<u8>
where
    T: PrimInt + CheckedShl,
{
    let mask = match T::one().checked_shl((end - start) as u32) {
        Some(shl) => (shl - T::one()) << (start as u32),
        None => T::max_value() << (start as u32),
    };
    let masked = value & mask;
    if masked.is_zero() {
        None
    } else {
        let leading_zeroes = masked.leading_zeros();
        let total_bits = std::mem::size_of::<T>() * u8::BITS as usize;
        Some(total_bits as u8 - leading_zeroes as u8 - 1)
    }
}

If it is not worth writing a generic function, a simpler solution is using find_last_non_zero_bit_usize for both u8 and usize. We just cast the u8 to usize and set start and end to 0 and 8. It involves a zero extension from u8 to usize, but is usually a no-op for most architectures because compilers usually use a whole 32-bit or 64-bit register to hold a u8 variable anyway. And because it is only done in the first or last incomplete byte, the cost is negligible.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. I used your implemenation. Thanks.

None => <$int_ty>::MAX << $start,
};
let leading_zeroes = ($value & mask).leading_zeros();
if leading_zeroes >= <$int_ty>::BITS {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can just compare $value & mask with zero. If not zero, there must be at least one non-zero bit.

let step = if cur.is_aligned_to(BYTES_IN_ADDRESS)
&& cur.align_down(BYTES_IN_ADDRESS) >= meta_start
{
let step = if cur.is_aligned_to(BYTES_IN_ADDRESS) && cur - BYTES_IN_ADDRESS >= meta_start {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the other fix that is needed. The old code may check an address that is below meta_start.

Copy link
Collaborator

@wks wks left a comment

Choose a reason for hiding this comment

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

LGTM

@qinsoon qinsoon added this pull request to the merge queue Aug 28, 2024
Merged via the queue into mmtk:master with commit 963e9c2 Aug 28, 2024
@qinsoon qinsoon deleted the fix-find-prev-non-zero-value branch August 28, 2024 06:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants