about summary refs log tree commit diff
path: root/tests/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'tests/codegen')
-rw-r--r--tests/codegen/checked_math.rs86
-rw-r--r--tests/codegen/mem-replace-simple-type.rs6
-rw-r--r--tests/codegen/slice-ref-equality.rs32
-rw-r--r--tests/codegen/vec-len-invariant.rs16
4 files changed, 122 insertions, 18 deletions
diff --git a/tests/codegen/checked_math.rs b/tests/codegen/checked_math.rs
new file mode 100644
index 00000000000..41016e3b7be
--- /dev/null
+++ b/tests/codegen/checked_math.rs
@@ -0,0 +1,86 @@
+//@ compile-flags: -O -Z merge-functions=disabled
+
+#![crate_type = "lib"]
+#![feature(unchecked_shifts)]
+
+// Because the result of something like `u32::checked_sub` can only be used if it
+// didn't overflow, make sure that LLVM actually knows that in optimized builds.
+// Thanks to poison semantics, this doesn't even need branches.
+
+// CHECK-LABEL: @checked_sub_unsigned
+// CHECK-SAME: (i16 noundef %a, i16 noundef %b)
+#[no_mangle]
+pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp uge i16 %a, %b
+    // CHECK-DAG: %[[DIFF_P:.+]] = sub nuw i16 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i16
+    // CHECK-DAG: %[[DIFF_U:.+]] = select i1 %[[IS_SOME]], i16 %[[DIFF_P]], i16 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i16, i16 } poison, i16 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i16, i16 } %[[R0]], i16 %[[DIFF_U]], 1
+    // CHECK: ret { i16, i16 } %[[R1]]
+    a.checked_sub(b)
+}
+
+// Note that `shl` and `shr` in LLVM are already unchecked. So rather than
+// looking for no-wrap flags, we just need there to not be any masking.
+
+// CHECK-LABEL: @checked_shl_unsigned
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = shl i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shl(b)
+}
+
+// CHECK-LABEL: @checked_shr_unsigned
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = lshr i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shr(b)
+}
+
+// CHECK-LABEL: @checked_shl_signed
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = shl i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shl(b)
+}
+
+// CHECK-LABEL: @checked_shr_signed
+// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+#[no_mangle]
+pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
+    // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
+    // CHECK-DAG: %[[SHIFTED_P:.+]] = ashr i32 %a, %b
+    // CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32
+    // CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef
+
+    // CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0
+    // CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1
+    // CHECK: ret { i32, i32 } %[[R1]]
+    a.checked_shr(b)
+}
diff --git a/tests/codegen/mem-replace-simple-type.rs b/tests/codegen/mem-replace-simple-type.rs
index 50b43f5854a..7209fa21925 100644
--- a/tests/codegen/mem-replace-simple-type.rs
+++ b/tests/codegen/mem-replace-simple-type.rs
@@ -34,18 +34,20 @@ pub fn replace_ref_str<'a>(r: &mut &'a str, v: &'a str) -> &'a str {
 
 #[no_mangle]
 // CHECK-LABEL: @replace_short_array_3(
+// CHECK-SAME: ptr{{.+}}sret{{.+}}%[[RET:.+]], ptr{{.+}}%r, ptr{{.+}}%v
 pub fn replace_short_array_3(r: &mut [u32; 3], v: [u32; 3]) -> [u32; 3] {
     // CHECK-NOT: alloca
-    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %result, ptr align 4 %r, i64 12, i1 false)
+    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[RET]], ptr align 4 %r, i64 12, i1 false)
     // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %r, ptr align 4 %v, i64 12, i1 false)
     std::mem::replace(r, v)
 }
 
 #[no_mangle]
 // CHECK-LABEL: @replace_short_array_4(
+// CHECK-SAME: ptr{{.+}}sret{{.+}}%[[RET:.+]], ptr{{.+}}%r, ptr{{.+}}%v
 pub fn replace_short_array_4(r: &mut [u32; 4], v: [u32; 4]) -> [u32; 4] {
     // CHECK-NOT: alloca
-    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %result, ptr align 4 %r, i64 16, i1 false)
+    // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[RET]], ptr align 4 %r, i64 16, i1 false)
     // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %r, ptr align 4 %v, i64 16, i1 false)
     std::mem::replace(r, v)
 }
diff --git a/tests/codegen/slice-ref-equality.rs b/tests/codegen/slice-ref-equality.rs
index 85d9c34a30b..7ab70108fe0 100644
--- a/tests/codegen/slice-ref-equality.rs
+++ b/tests/codegen/slice-ref-equality.rs
@@ -43,48 +43,48 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool {
 // equality for non-byte types also just emit a `bcmp`, not a loop.
 
 // CHECK-LABEL: @eq_slice_of_nested_u8(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %1, 3
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] {{%x.1|%y.1}}, 3
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
 }
 
 // CHECK-LABEL: @eq_slice_of_i32(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 2
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
 }
 
 // CHECK-LABEL: @eq_slice_of_nonzero(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_nonzero(x: &[NonZero<i32>], y: &[NonZero<i32>]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 2
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
 }
 
 // CHECK-LABEL: @eq_slice_of_option_of_nonzero(
-// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
-// CHECK-SAME: [[USIZE]] noundef %3
+// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
+// CHECK-SAME: [[USIZE]] noundef %y.1
 #[no_mangle]
 fn eq_slice_of_option_of_nonzero(x: &[Option<NonZero<i16>>], y: &[Option<NonZero<i16>>]) -> bool {
-    // CHECK: icmp eq [[USIZE]] %1, %3
-    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 1
+    // CHECK: icmp eq [[USIZE]] %x.1, %y.1
+    // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] {{%x.1|%y.1}}, 1
     // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}(ptr
     // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
     x == y
diff --git a/tests/codegen/vec-len-invariant.rs b/tests/codegen/vec-len-invariant.rs
new file mode 100644
index 00000000000..780c86bab95
--- /dev/null
+++ b/tests/codegen/vec-len-invariant.rs
@@ -0,0 +1,16 @@
+//@ compile-flags: -O
+//@ only-64bit
+//
+// This test confirms that we do not reload the length of a Vec after growing it in push.
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @should_load_once
+#[no_mangle]
+pub fn should_load_once(v: &mut Vec<u8>) {
+    // CHECK: load i64
+    // CHECK: call {{.*}}grow_one
+    // CHECK-NOT: load i64
+    // CHECK: add {{.*}}, 1
+    v.push(1);
+}