Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mem::replace don't actually mutate the destination on Android #49282

Closed
jdm opened this issue Mar 22, 2018 · 5 comments
Closed

mem::replace don't actually mutate the destination on Android #49282

jdm opened this issue Mar 22, 2018 · 5 comments
Labels
A-codegen Area: Code generation A-simd Area: SIMD (Single Instruction Multiple Data) C-bug Category: This is a bug. O-android Operating system: Android T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@jdm
Copy link
Contributor

jdm commented Mar 22, 2018

I have a branch of Servo which crashes when run on Android because a key use of mem::replace does not actually cause the destination to change values. When I add println!("{:?}", self.pending_line) before and after the mem::replace, on desktop the values are updated as expected, but on Android the original value remains.

When I use either of the following instead:

self.pending_line = Line::new(self.floats.writing_mode, &self.minimum_metrics);

or

let mut new_line = Line::new(self.floats.writing_mode, &self.minimum_metrics);
mem::swap(&mut self.pending_line, &mut new_line);

then self.pending is updated as expected and Servo does not crash on Android.

@jdm
Copy link
Contributor Author

jdm commented Mar 22, 2018

The following also works:

        mem::replace(&mut self.pending_line,
                     Line::new(self.floats.writing_mode, &self.minimum_metrics));
        mem::replace(&mut self.pending_line,
                     Line::new(self.floats.writing_mode, &self.minimum_metrics));

(yes, performing the same operation twice)

@kennytm kennytm added A-codegen Area: Code generation O-android Operating system: Android C-bug Category: This is a bug. labels Mar 22, 2018
@scottmcm
Copy link
Member

Hmm, it looks like that type is pretty large (https://github.com/jdm/servo/blob/layoutstuff/components/layout/inline.rs#L221-L245) so will be hitting the simd path in swap (https://github.com/rust-lang/rust/blob/master/src/libcore/ptr.rs#L194-L227) that's currently disabled on some other platforms, so I wonder if the bug's in there somewhere...

@jonas-schievink jonas-schievink added A-simd Area: SIMD (Single Instruction Multiple Data) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 20, 2020
@jonas-schievink
Copy link
Contributor

Triage: Not sure if this is still an issue.

Updated libcore SIMD code:

rust/src/libcore/ptr/mod.rs

Lines 418 to 465 in 8ce3f84

#[inline]
unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
// The approach here is to utilize simd to swap x & y efficiently. Testing reveals
// that swapping either 32 bytes or 64 bytes at a time is most efficient for Intel
// Haswell E processors. LLVM is more able to optimize if we give a struct a
// #[repr(simd)], even if we don't actually use this struct directly.
//
// FIXME repr(simd) broken on emscripten and redox
#[cfg_attr(not(any(target_os = "emscripten", target_os = "redox")), repr(simd))]
struct Block(u64, u64, u64, u64);
struct UnalignedBlock(u64, u64, u64, u64);
let block_size = mem::size_of::<Block>();
// Loop through x & y, copying them `Block` at a time
// The optimizer should unroll the loop fully for most types
// N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively
let mut i = 0;
while i + block_size <= len {
// Create some uninitialized memory as scratch space
// Declaring `t` here avoids aligning the stack when this loop is unused
let mut t = mem::MaybeUninit::<Block>::uninit();
let t = t.as_mut_ptr() as *mut u8;
let x = x.add(i);
let y = y.add(i);
// Swap a block of bytes of x & y, using t as a temporary buffer
// This should be optimized into efficient SIMD operations where available
copy_nonoverlapping(x, t, block_size);
copy_nonoverlapping(y, x, block_size);
copy_nonoverlapping(t, y, block_size);
i += block_size;
}
if i < len {
// Swap any remaining bytes
let mut t = mem::MaybeUninit::<UnalignedBlock>::uninit();
let rem = len - i;
let t = t.as_mut_ptr() as *mut u8;
let x = x.add(i);
let y = y.add(i);
copy_nonoverlapping(x, t, rem);
copy_nonoverlapping(y, x, rem);
copy_nonoverlapping(t, y, rem);
}
}

@jdm
Copy link
Contributor Author

jdm commented Mar 4, 2023

Realistically I don't think there's any point to leaving this open. We never tried to make a minimal reproduction, and nobody else has stumbled across this issue in the intervening five years.

@jdm jdm closed this as completed Mar 4, 2023
@scottmcm
Copy link
Member

scottmcm commented Mar 4, 2023

Agreed; the implementation of replace is also very different now after #83022

unsafe {
let result = ptr::read(dest);
ptr::write(dest, src);
result
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation A-simd Area: SIMD (Single Instruction Multiple Data) C-bug Category: This is a bug. O-android Operating system: Android T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants