diff options
| author | Scott McMurray <scottmcm@users.noreply.github.com> | 2023-03-11 15:32:54 -0800 | 
|---|---|---|
| committer | Scott McMurray <scottmcm@users.noreply.github.com> | 2023-03-11 17:44:43 -0800 | 
| commit | b2c717fa338dd3e917008484c4bf55886041f743 (patch) | |
| tree | c8c203c011ef038f7a2ddeb0a9c09b760a9def4d /tests/codegen/mem-replace-direct-memcpy.rs | |
| parent | 19c53768af6e48514238e4224b5bf5ecd51bc7b1 (diff) | |
| download | rust-b2c717fa338dd3e917008484c4bf55886041f743.tar.gz rust-b2c717fa338dd3e917008484c4bf55886041f743.zip | |
`MaybeUninit::assume_init_read` should have `noundef` load metadata
I was looking into `array::IntoIter` optimization, and noticed that it wasn't annotating the loads with `noundef` for simple things like `array::IntoIter<i32, N>`. Turned out to be a more general problem as `MaybeUninit::assume_init_read` isn't marking the load as initialized (<https://rust.godbolt.org/z/Mxd8TPTnv>), which is unfortunate since that's basically its reason to exist. This PR lowers `ptr::read(p)` to `copy *p` in MIR, which fortuitiously also improves the IR we give to LLVM for things like `mem::replace`.
Diffstat (limited to 'tests/codegen/mem-replace-direct-memcpy.rs')
| -rw-r--r-- | tests/codegen/mem-replace-direct-memcpy.rs | 41 | 
1 files changed, 36 insertions, 5 deletions
| diff --git a/tests/codegen/mem-replace-direct-memcpy.rs b/tests/codegen/mem-replace-direct-memcpy.rs index e8bbf0e1bbd..3b01a621b56 100644 --- a/tests/codegen/mem-replace-direct-memcpy.rs +++ b/tests/codegen/mem-replace-direct-memcpy.rs @@ -12,13 +12,44 @@ pub fn replace_byte(dst: &mut u8, src: u8) -> u8 { std::mem::replace(dst, src) } +#[repr(C, align(8))] +pub struct Big([u64; 7]); +pub fn replace_big(dst: &mut Big, src: Big) -> Big { + // Before the `read_via_copy` intrinsic, this emitted six `memcpy`s. + std::mem::replace(dst, src) +} + // NOTE(eddyb) the `CHECK-NOT`s ensure that the only calls of `@llvm.memcpy` in -// the entire output, are the two direct calls we want, from `ptr::replace`. +// the entire output, are the direct calls we want, from `ptr::replace`. // CHECK-NOT: call void @llvm.memcpy -// CHECK: ; core::mem::replace -// CHECK-NOT: call void @llvm.memcpy -// CHECK: call void @llvm.memcpy.{{.+}}({{i8\*|ptr}} align 1 %{{.*}}, {{i8\*|ptr}} align 1 %{{.*}}, i{{.*}} 1, i1 false) + +// For a large type, we expect exactly three `memcpy`s +// CHECK-LABEL: define internal void @{{.+}}mem{{.+}}replace{{.+}}sret(%Big) + // CHECK-NOT: alloca + // CHECK: alloca %Big + // CHECK-NOT: alloca + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.+}}({{i8\*|ptr}} align 8 %{{.*}}, {{i8\*|ptr}} align 8 %{{.*}}, i{{.*}} 56, i1 false) + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.+}}({{i8\*|ptr}} align 8 %{{.*}}, {{i8\*|ptr}} align 8 %{{.*}}, i{{.*}} 56, i1 false) + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.+}}({{i8\*|ptr}} align 8 %{{.*}}, {{i8\*|ptr}} align 8 %{{.*}}, i{{.*}} 56, i1 false) + // CHECK-NOT: call void @llvm.memcpy + // CHECK-NOT: call void @llvm.memcpy -// CHECK: call void @llvm.memcpy.{{.+}}({{i8\*|ptr}} align 1 %{{.*}}, {{i8\*|ptr}} align 1 %{{.*}}, i{{.*}} 1, i1 false) + +// For a small type, we expect one each of `load`/`store`/`memcpy` instead +// CHECK-LABEL: define internal noundef i8 @{{.+}}mem{{.+}}replace + // CHECK-NOT: alloca + // CHECK: alloca i8 + // CHECK-NOT: alloca + // CHECK-NOT: call void @llvm.memcpy + // CHECK: load i8 + // CHECK-NOT: call void @llvm.memcpy + // CHECK: store i8 + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.+}}({{i8\*|ptr}} align 1 %{{.*}}, {{i8\*|ptr}} align 1 %{{.*}}, i{{.*}} 1, i1 false) + // CHECK-NOT: call void @llvm.memcpy + // CHECK-NOT: call void @llvm.memcpy | 
