Skip to content

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

New issue

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

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

Already on GitHub? # to your account

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
}

# for free to join this conversation on GitHub. Already have an account? # 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