c2fd26a115
Currently, we assume that ScalarPair is always represented using a two-element struct, both as an immediate value and when stored in memory. This currently works fairly well, but runs into problems with https://github.com/rust-lang/rust/pull/116672, where a ScalarPair involving an i128 type can no longer be represented as a two-element struct in memory. For example, the tuple `(i32, i128)` needs to be represented in-memory as `{ i32, [3 x i32], i128 }` to satisfy alignment requirement. Using `{ i32, i128 }` instead will result in the second element being stored at the wrong offset (prior to LLVM 18). Resolve this issue by no longer requiring that the immediate and in-memory type for ScalarPair are the same. The in-memory type will now look the same as for normal struct types (and will include padding filler and similar), while the immediate type stays a simple two-element struct type. This also means that booleans in immediate ScalarPair are now represented as i1 rather than i8, just like we do everywhere else. The core change here is to llvm_type (which now treats ScalarPair as a normal struct) and immediate_llvm_type (which returns the two-element struct that llvm_type used to produce). The rest is fixing things up to no longer assume these are the same. In particular, this switches places that try to get pointers to the ScalarPair elements to use byte-geps instead of struct-geps.
117 lines
4.0 KiB
Rust
117 lines
4.0 KiB
Rust
// no-system-llvm
|
|
// compile-flags: -O
|
|
// ignore-debug (these add extra checks that make it hard to verify)
|
|
#![crate_type = "lib"]
|
|
#![feature(exact_size_is_empty)]
|
|
|
|
// The slice iterator used to `assume` that the `start` pointer was non-null.
|
|
// That ought to be unneeded, though, since the type is `NonNull`, so this test
|
|
// confirms that the appropriate metadata is included to denote that.
|
|
|
|
// It also used to `assume` the `end` pointer was non-null, but that's no longer
|
|
// needed as the code changed to read it as a `NonNull`, and thus gets the
|
|
// appropriate `!nonnull` annotations naturally.
|
|
|
|
// CHECK-LABEL: @slice_iter_next(
|
|
#[no_mangle]
|
|
pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
|
|
// CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
|
|
// CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
// CHECK: %[[START:.+]] = load ptr, ptr %it,
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
// CHECK: icmp eq ptr %[[START]], %[[END]]
|
|
|
|
// CHECK: store ptr{{.+}}, ptr %it,
|
|
|
|
it.next()
|
|
}
|
|
|
|
// CHECK-LABEL: @slice_iter_next_back(
|
|
#[no_mangle]
|
|
pub fn slice_iter_next_back<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
|
|
// CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
|
|
// CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
// CHECK: %[[START:.+]] = load ptr, ptr %it,
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
// CHECK: icmp eq ptr %[[START]], %[[END]]
|
|
|
|
// CHECK: store ptr{{.+}}, ptr %[[ENDP]],
|
|
|
|
it.next_back()
|
|
}
|
|
|
|
// The slice iterator `new` methods used to `assume` that the pointer is non-null,
|
|
// but passing slices already requires that, to the extent that LLVM actually
|
|
// removed the `call @llvm.assume` anyway. These tests just demonstrate that the
|
|
// attribute is there, and confirms adding the assume back doesn't do anything.
|
|
|
|
// CHECK-LABEL: @slice_iter_new
|
|
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1)
|
|
#[no_mangle]
|
|
pub fn slice_iter_new(slice: &[u32]) -> std::slice::Iter<'_, u32> {
|
|
// CHECK-NOT: slice
|
|
// CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1
|
|
// CHECK-NOT: slice
|
|
// CHECK: insertvalue {{.+}} ptr %slice.0, 0
|
|
// CHECK-NOT: slice
|
|
// CHECK: insertvalue {{.+}} ptr %[[END]], 1
|
|
// CHECK-NOT: slice
|
|
// CHECK: }
|
|
slice.iter()
|
|
}
|
|
|
|
// CHECK-LABEL: @slice_iter_mut_new
|
|
// CHECK-SAME: (ptr noalias noundef nonnull {{.+}} %slice.0, {{.+}} noundef %slice.1)
|
|
#[no_mangle]
|
|
pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
|
|
// CHECK-NOT: slice
|
|
// CHECK: %[[END:.+]] = getelementptr inbounds i32{{.+}} %slice.0{{.+}} %slice.1
|
|
// CHECK-NOT: slice
|
|
// CHECK: insertvalue {{.+}} ptr %slice.0, 0
|
|
// CHECK-NOT: slice
|
|
// CHECK: insertvalue {{.+}} ptr %[[END]], 1
|
|
// CHECK-NOT: slice
|
|
// CHECK: }
|
|
slice.iter_mut()
|
|
}
|
|
|
|
// CHECK-LABEL: @slice_iter_is_empty
|
|
#[no_mangle]
|
|
pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool {
|
|
// CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
|
|
// CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
// CHECK: %[[START:.+]] = load ptr, ptr %it,
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
|
|
// CHECK: %[[RET:.+]] = icmp eq ptr %[[START]], %[[END]]
|
|
// CHECK: ret i1 %[[RET]]
|
|
it.is_empty()
|
|
}
|
|
|
|
// CHECK-LABEL: @slice_iter_len
|
|
#[no_mangle]
|
|
pub fn slice_iter_len(it: &std::slice::Iter<'_, u32>) -> usize {
|
|
// CHECK: %[[ENDP:.+]] = getelementptr inbounds i8, ptr %it, {{i32 4|i64 8}}
|
|
// CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
// CHECK: %[[START:.+]] = load ptr, ptr %it,
|
|
// CHECK-SAME: !nonnull
|
|
// CHECK-SAME: !noundef
|
|
|
|
// CHECK: ptrtoint
|
|
// CHECK: ptrtoint
|
|
// CHECK: sub nuw
|
|
// CHECK: lshr exact
|
|
it.len()
|
|
}
|